aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/address.c3
-rw-r--r--src/common/compat.c105
-rw-r--r--src/common/compat.h2
-rw-r--r--src/common/compat_libevent.c35
-rw-r--r--src/common/compat_libevent.h37
-rw-r--r--src/common/compat_openssl.h37
-rw-r--r--src/common/container.c24
-rw-r--r--src/common/crypto.c233
-rw-r--r--src/common/crypto.h38
-rw-r--r--src/common/crypto_curve25519.c3
-rw-r--r--src/common/include.am1
-rw-r--r--src/common/log.c17
-rw-r--r--src/common/procmon.c3
-rw-r--r--src/common/torlog.h17
-rw-r--r--src/common/tortls.c416
-rw-r--r--src/common/tortls.h116
-rw-r--r--src/common/util.c104
-rw-r--r--src/common/util.h1
18 files changed, 652 insertions, 540 deletions
diff --git a/src/common/address.c b/src/common/address.c
index cfa8fd1dca..aef229b02c 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1469,7 +1469,8 @@ get_interface_addresses_ioctl(int severity)
int fd;
smartlist_t *result = NULL;
- /* This interface, AFAICT, only supports AF_INET addresses */
+ /* This interface, AFAICT, only supports AF_INET addresses,
+ * except on AIX. For Solaris, we could use SIOCGLIFCONF. */
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) {
tor_log(severity, LD_NET, "socket failed: %s", strerror(errno));
diff --git a/src/common/compat.c b/src/common/compat.c
index 7d72b4b7fd..55fb55a045 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1486,6 +1486,20 @@ tor_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
}
#ifdef NEED_ERSATZ_SOCKETPAIR
+
+static INLINE socklen_t
+SIZEOF_SOCKADDR(int domain)
+{
+ switch (domain) {
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ default:
+ return 0;
+ }
+}
+
/**
* Helper used to implement socketpair on systems that lack it, by
* making a direct connection to localhost.
@@ -1501,13 +1515,21 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_socket_t listener = TOR_INVALID_SOCKET;
tor_socket_t connector = TOR_INVALID_SOCKET;
tor_socket_t acceptor = TOR_INVALID_SOCKET;
- struct sockaddr_in listen_addr;
- struct sockaddr_in connect_addr;
+ tor_addr_t listen_tor_addr;
+ struct sockaddr_storage connect_addr_ss, listen_addr_ss;
+ struct sockaddr *listen_addr = (struct sockaddr *) &listen_addr_ss;
+ uint16_t listen_port = 0;
+ tor_addr_t connect_tor_addr;
+ uint16_t connect_port = 0;
+ struct sockaddr *connect_addr = (struct sockaddr *) &connect_addr_ss;
socklen_t size;
int saved_errno = -1;
+ int ersatz_domain = AF_INET;
- memset(&connect_addr, 0, sizeof(connect_addr));
- memset(&listen_addr, 0, sizeof(listen_addr));
+ memset(&connect_tor_addr, 0, sizeof(connect_tor_addr));
+ memset(&connect_addr_ss, 0, sizeof(connect_addr_ss));
+ memset(&listen_tor_addr, 0, sizeof(listen_tor_addr));
+ memset(&listen_addr_ss, 0, sizeof(listen_addr_ss));
if (protocol
#ifdef AF_UNIX
@@ -1524,47 +1546,71 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
return -EINVAL;
}
- listener = tor_open_socket(AF_INET, type, 0);
- if (!SOCKET_OK(listener))
- return -tor_socket_errno(-1);
- memset(&listen_addr, 0, sizeof(listen_addr));
- listen_addr.sin_family = AF_INET;
- listen_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- listen_addr.sin_port = 0; /* kernel chooses port. */
- if (bind(listener, (struct sockaddr *) &listen_addr, sizeof (listen_addr))
- == -1)
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ int first_errno = tor_socket_errno(-1);
+ if (first_errno == SOCK_ERRNO(EPROTONOSUPPORT)
+ && ersatz_domain == AF_INET) {
+ /* Assume we're on an IPv6-only system */
+ ersatz_domain = AF_INET6;
+ listener = tor_open_socket(ersatz_domain, type, 0);
+ if (!SOCKET_OK(listener)) {
+ /* Keep the previous behaviour, which was to return the IPv4 error.
+ * (This may be less informative on IPv6-only systems.)
+ * XX/teor - is there a better way to decide which errno to return?
+ * (I doubt we care much either way, once there is an error.)
+ */
+ return -first_errno;
+ }
+ }
+ }
+ /* If there is no 127.0.0.1 or ::1, this will and must fail. Otherwise, we
+ * risk exposing a socketpair on a routable IP address. (Some BSD jails
+ * use a routable address for localhost. Fortunately, they have the real
+ * AF_UNIX socketpair.) */
+ if (ersatz_domain == AF_INET) {
+ tor_addr_from_ipv4h(&listen_tor_addr, INADDR_LOOPBACK);
+ } else {
+ tor_addr_parse(&listen_tor_addr, "[::1]");
+ }
+ tor_assert(tor_addr_is_loopback(&listen_tor_addr));
+ size = tor_addr_to_sockaddr(&listen_tor_addr,
+ 0 /* kernel chooses port. */,
+ listen_addr,
+ sizeof(listen_addr_ss));
+ if (bind(listener, listen_addr, size) == -1)
goto tidy_up_and_fail;
if (listen(listener, 1) == -1)
goto tidy_up_and_fail;
- connector = tor_open_socket(AF_INET, type, 0);
+ connector = tor_open_socket(ersatz_domain, type, 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);
- if (getsockname(listener, (struct sockaddr *) &connect_addr, &size) == -1)
+ size = sizeof(connect_addr_ss);
+ if (getsockname(listener, connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr))
+ if (size != SIZEOF_SOCKADDR (connect_addr->sa_family))
goto abort_tidy_up_and_fail;
- if (connect(connector, (struct sockaddr *) &connect_addr,
- sizeof(connect_addr)) == -1)
+ if (connect(connector, connect_addr, size) == -1)
goto tidy_up_and_fail;
- size = sizeof(listen_addr);
- acceptor = tor_accept_socket(listener,
- (struct sockaddr *) &listen_addr, &size);
+ size = sizeof(listen_addr_ss);
+ acceptor = tor_accept_socket(listener, listen_addr, &size);
if (!SOCKET_OK(acceptor))
goto tidy_up_and_fail;
- if (size != sizeof(listen_addr))
+ if (size != SIZEOF_SOCKADDR(listen_addr->sa_family))
goto abort_tidy_up_and_fail;
/* Now check we are talking to ourself by matching port and host on the
two sockets. */
- if (getsockname(connector, (struct sockaddr *) &connect_addr, &size) == -1)
+ if (getsockname(connector, connect_addr, &size) == -1)
goto tidy_up_and_fail;
- if (size != sizeof (connect_addr)
- || listen_addr.sin_family != connect_addr.sin_family
- || listen_addr.sin_addr.s_addr != connect_addr.sin_addr.s_addr
- || listen_addr.sin_port != connect_addr.sin_port) {
+ /* Set *_tor_addr and *_port to the address and port that was used */
+ tor_addr_from_sockaddr(&listen_tor_addr, listen_addr, &listen_port);
+ tor_addr_from_sockaddr(&connect_tor_addr, connect_addr, &connect_port);
+ if (size != SIZEOF_SOCKADDR (connect_addr->sa_family)
+ || tor_addr_compare(&listen_tor_addr, &connect_tor_addr, CMP_SEMANTIC)
+ || listen_port != connect_port) {
goto abort_tidy_up_and_fail;
}
tor_close_socket(listener);
@@ -1590,6 +1636,9 @@ tor_ersatz_socketpair(int family, int type, int protocol, tor_socket_t fd[2])
tor_close_socket(acceptor);
return -saved_errno;
}
+
+#undef SIZEOF_SOCKADDR
+
#endif
/* Return the maximum number of allowed sockets. */
diff --git a/src/common/compat.h b/src/common/compat.h
index c7c468c754..c3d6abd07c 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -118,6 +118,7 @@
#define ATTR_CONST __attribute__((const))
#define ATTR_MALLOC __attribute__((malloc))
#define ATTR_NORETURN __attribute__((noreturn))
+#define ATTR_WUR __attribute__((warn_unused_result))
/* Alas, nonnull is not at present a good idea for us. We'd like to get
* warnings when we pass NULL where we shouldn't (which nonnull does, albeit
* spottily), but we don't want to tell the compiler to make optimizations
@@ -153,6 +154,7 @@
#define ATTR_NORETURN
#define ATTR_NONNULL(x)
#define ATTR_UNUSED
+#define ATTR_WUR
#define PREDICT_LIKELY(exp) (exp)
#define PREDICT_UNLIKELY(exp) (exp)
#endif
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index a366b6c9c6..29e5c5f63c 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -11,6 +11,7 @@
#include "orconfig.h"
#include "compat.h"
+#define COMPAT_LIBEVENT_PRIVATE
#include "compat_libevent.h"
#include "crypto.h"
@@ -28,39 +29,11 @@
#include <event.h>
#endif
-/** A number representing a version of Libevent.
-
- This is a 4-byte number, with the first three bytes representing the
- major, minor, and patchlevel respectively of the library. The fourth
- byte is unused.
-
- This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
- 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
- format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
- to be equivalent to 1.0.1, and so on.
-*/
-typedef uint32_t le_version_t;
-
-/** @{ */
-/** Macros: returns the number of a libevent version as a le_version_t */
-#define V(major, minor, patch) \
- (((major) << 24) | ((minor) << 16) | ((patch) << 8))
-#define V_OLD(major, minor, patch) \
- V((major), (minor), (patch)-'a'+1)
-/** @} */
-
-/** Represetns a version of libevent so old we can't figure out what version
- * it is. */
-#define LE_OLD V(0,0,0)
-/** Represents a version of libevent so weird we can't figure out what version
- * it is. */
-#define LE_OTHER V(0,0,99)
-
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
/** Callback function passed to event_set_log() so we can intercept
* log messages from libevent. */
-static void
+STATIC void
libevent_logging_callback(int severity, const char *msg)
{
char buf[1024];
@@ -291,7 +264,7 @@ tor_libevent_get_method(void)
/** Return the le_version_t for the version of libevent specified in the
* string <b>v</b>. If the version is very new or uses an unrecognized
* version, format, return LE_OTHER. */
-static le_version_t
+STATIC le_version_t
tor_decode_libevent_version(const char *v)
{
unsigned major, minor, patchlevel;
@@ -322,7 +295,7 @@ tor_decode_libevent_version(const char *v)
* Two different versions with different numbers are sure not to be binary
* compatible. Two different versions with the same numbers have a decent
* chance of binary compatibility.*/
-static int
+STATIC int
le_versions_compatibility(le_version_t v)
{
if (v == LE_OTHER)
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 39181efb7b..8ee02c0b6d 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -91,5 +91,42 @@ void tor_gettimeofday_cache_set(const struct timeval *tv);
#endif
void tor_gettimeofday_cached_monotonic(struct timeval *tv);
+#ifdef COMPAT_LIBEVENT_PRIVATE
+/** A number representing a version of Libevent.
+
+ This is a 4-byte number, with the first three bytes representing the
+ major, minor, and patchlevel respectively of the library. The fourth
+ byte is unused.
+
+ This is equivalent to the format of LIBEVENT_VERSION_NUMBER on Libevent
+ 2.0.1 or later. For versions of Libevent before 1.4.0, which followed the
+ format of "1.0, 1.0a, 1.0b", we define 1.0 to be equivalent to 1.0.0, 1.0a
+ to be equivalent to 1.0.1, and so on.
+*/
+typedef uint32_t le_version_t;
+
+/** @{ */
+/** Macros: returns the number of a libevent version as a le_version_t */
+#define V(major, minor, patch) \
+ (((major) << 24) | ((minor) << 16) | ((patch) << 8))
+#define V_OLD(major, minor, patch) \
+ V((major), (minor), (patch)-'a'+1)
+/** @} */
+
+/** Represetns a version of libevent so old we can't figure out what version
+ * it is. */
+#define LE_OLD V(0,0,0)
+/** Represents a version of libevent so weird we can't figure out what version
+ * it is. */
+#define LE_OTHER V(0,0,99)
+
+STATIC void
+libevent_logging_callback(int severity, const char *msg);
+STATIC le_version_t
+tor_decode_libevent_version(const char *v);
+STATIC int
+le_versions_compatibility(le_version_t v);
+#endif
+
#endif
diff --git a/src/common/compat_openssl.h b/src/common/compat_openssl.h
new file mode 100644
index 0000000000..3fcd684c0c
--- /dev/null
+++ b/src/common/compat_openssl.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2001, Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_COMPAT_OPENSSL_H
+#define TOR_COMPAT_OPENSSL_H
+
+#include <openssl/opensslv.h>
+
+/**
+ * \file compat_openssl.h
+ *
+ * \brief compatability definitions for working with different openssl forks
+ **/
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
+#define OPENSSL_VERSION SSLEAY_VERSION
+#define OpenSSL_version(v) SSLeay_version(v)
+#define OpenSSL_version_num() SSLeay()
+#define RAND_OpenSSL() RAND_SSLeay()
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ (((st) == SSL3_ST_SW_SRVR_HELLO_A) || \
+ ((st) == SSL3_ST_SW_SRVR_HELLO_B))
+#define OSSL_HANDSHAKE_STATE int
+#else
+#define STATE_IS_SW_SERVER_HELLO(st) \
+ ((st) == TLS_ST_SW_SRVR_HELLO)
+#endif
+
+#endif
+
diff --git a/src/common/container.c b/src/common/container.c
index 636dfb6c57..c6f059170e 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -55,6 +55,7 @@ smartlist_free,(smartlist_t *sl))
void
smartlist_clear(smartlist_t *sl)
{
+ memset(sl->list, 0, sizeof(void *) * sl->num_used);
sl->num_used = 0;
}
@@ -82,9 +83,11 @@ smartlist_ensure_capacity(smartlist_t *sl, int size)
while (size > higher)
higher *= 2;
}
- sl->capacity = higher;
sl->list = tor_reallocarray(sl->list, sizeof(void *),
- ((size_t)sl->capacity));
+ ((size_t)higher));
+ memset(sl->list + sl->capacity, 0,
+ sizeof(void *) * (higher - sl->capacity));
+ sl->capacity = higher;
}
#undef ASSERT_CAPACITY
#undef MAX_CAPACITY
@@ -123,6 +126,7 @@ smartlist_remove(smartlist_t *sl, const void *element)
if (sl->list[i] == element) {
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
@@ -132,9 +136,11 @@ void *
smartlist_pop_last(smartlist_t *sl)
{
tor_assert(sl);
- if (sl->num_used)
- return sl->list[--sl->num_used];
- else
+ if (sl->num_used) {
+ void *tmp = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
+ return tmp;
+ } else
return NULL;
}
@@ -165,6 +171,7 @@ smartlist_string_remove(smartlist_t *sl, const char *element)
tor_free(sl->list[i]);
sl->list[i] = sl->list[--sl->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl->list[sl->num_used] = NULL;
}
}
}
@@ -321,6 +328,7 @@ smartlist_intersect(smartlist_t *sl1, const smartlist_t *sl2)
if (!smartlist_contains(sl2, sl1->list[i])) {
sl1->list[i] = sl1->list[--sl1->num_used]; /* swap with the end */
i--; /* so we process the new i'th element */
+ sl1->list[sl1->num_used] = NULL;
}
}
@@ -345,6 +353,7 @@ smartlist_del(smartlist_t *sl, int idx)
tor_assert(idx>=0);
tor_assert(idx < sl->num_used);
sl->list[idx] = sl->list[--sl->num_used];
+ sl->list[sl->num_used] = NULL;
}
/** Remove the <b>idx</b>th element of sl; if idx is not the last element,
@@ -360,6 +369,7 @@ smartlist_del_keeporder(smartlist_t *sl, int idx)
--sl->num_used;
if (idx < sl->num_used)
memmove(sl->list+idx, sl->list+idx+1, sizeof(void*)*(sl->num_used-idx));
+ sl->list[sl->num_used] = NULL;
}
/** Insert the value <b>val</b> as the new <b>idx</b>th element of
@@ -937,9 +947,11 @@ smartlist_pqueue_pop(smartlist_t *sl,
*IDXP(top)=-1;
if (--sl->num_used) {
sl->list[0] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(0);
smartlist_heapify(sl, compare, idx_field_offset, 0);
}
+ sl->list[sl->num_used] = NULL;
return top;
}
@@ -959,9 +971,11 @@ smartlist_pqueue_remove(smartlist_t *sl,
--sl->num_used;
*IDXP(item) = -1;
if (idx == sl->num_used) {
+ sl->list[sl->num_used] = NULL;
return;
} else {
sl->list[idx] = sl->list[sl->num_used];
+ sl->list[sl->num_used] = NULL;
UPDATE_IDX(idx);
smartlist_heapify(sl, compare, idx_field_offset, idx);
}
diff --git a/src/common/crypto.c b/src/common/crypto.c
index 815c2ec0c5..baef755d00 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -21,18 +21,13 @@
#undef OCSP_RESPONSE
#endif
-#include <openssl/opensslv.h>
-
#define CRYPTO_PRIVATE
#include "crypto.h"
+#include "compat_openssl.h"
#include "crypto_curve25519.h"
#include "crypto_ed25519.h"
#include "crypto_format.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
-
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
@@ -227,7 +222,7 @@ const char *
crypto_openssl_get_version_str(void)
{
if (crypto_openssl_version_str == NULL) {
- const char *raw_version = SSLeay_version(SSLEAY_VERSION);
+ const char *raw_version = OpenSSL_version(OPENSSL_VERSION);
crypto_openssl_version_str = parse_openssl_version_str(raw_version);
}
return crypto_openssl_version_str;
@@ -251,11 +246,13 @@ crypto_openssl_get_header_version_str(void)
static int
crypto_force_rand_ssleay(void)
{
- if (RAND_get_rand_method() != RAND_SSLeay()) {
+ RAND_METHOD *default_method;
+ default_method = RAND_OpenSSL();
+ if (RAND_get_rand_method() != default_method) {
log_notice(LD_CRYPTO, "It appears that one of our engines has provided "
"a replacement the OpenSSL RNG. Resetting it to the default "
"implementation.");
- RAND_set_rand_method(RAND_SSLeay());
+ RAND_set_rand_method(default_method);
return 1;
}
return 0;
@@ -270,8 +267,7 @@ crypto_init_siphash_key(void)
if (have_seeded_siphash)
return 0;
- if (crypto_rand((char*) &key, sizeof(key)) < 0)
- return -1;
+ crypto_rand((char*) &key, sizeof(key));
siphash_set_global_key(&key);
have_seeded_siphash = 1;
return 0;
@@ -291,16 +287,18 @@ crypto_early_init(void)
setup_openssl_threading();
- if (SSLeay() == OPENSSL_VERSION_NUMBER &&
- !strcmp(SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_TEXT)) {
+ unsigned long version_num = OpenSSL_version_num();
+ const char *version_str = OpenSSL_version(OPENSSL_VERSION);
+ if (version_num == OPENSSL_VERSION_NUMBER &&
+ !strcmp(version_str, OPENSSL_VERSION_TEXT)) {
log_info(LD_CRYPTO, "OpenSSL version matches version from headers "
- "(%lx: %s).", SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ "(%lx: %s).", version_num, version_str);
} else {
log_warn(LD_CRYPTO, "OpenSSL version from headers does not match the "
"version we're running with. If you get weird crashes, that "
"might be why. (Compiled with %lx: %s; running with %lx: %s).",
(unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
- SSLeay(), SSLeay_version(SSLEAY_VERSION));
+ version_num, version_str);
}
crypto_force_rand_ssleay();
@@ -322,7 +320,8 @@ int
crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
{
if (!crypto_global_initialized_) {
- crypto_early_init();
+ if (crypto_early_init() < 0)
+ return -1;
crypto_global_initialized_ = 1;
@@ -404,11 +403,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
void
crypto_thread_cleanup(void)
{
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
@@ -432,9 +427,10 @@ crypto_pk_get_rsa_(crypto_pk_t *env)
}
/** 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_get_evp_pkey_(crypto_pk_t *env, int private)
+ * private is set, include the private-key portion of the key. Return a valid
+ * pointer on success, and NULL on failure. */
+MOCK_IMPL(EVP_PKEY *,
+ crypto_pk_get_evp_pkey_,(crypto_pk_t *env, int private))
{
RSA *key = NULL;
EVP_PKEY *pkey = NULL;
@@ -470,8 +466,8 @@ crypto_dh_get_dh_(crypto_dh_t *dh)
/** Allocate and return storage for a public key. The key itself will not yet
* be set.
*/
-crypto_pk_t *
-crypto_pk_new(void)
+MOCK_IMPL(crypto_pk_t *,
+ crypto_pk_new,(void))
{
RSA *rsa;
@@ -553,8 +549,8 @@ crypto_cipher_free(crypto_cipher_t *env)
/** Generate a <b>bits</b>-bit new public/private keypair in <b>env</b>.
* Return 0 on success, -1 on failure.
*/
-int
-crypto_pk_generate_key_with_bits(crypto_pk_t *env, int bits)
+MOCK_IMPL(int,
+ crypto_pk_generate_key_with_bits,(crypto_pk_t *env, int bits))
{
tor_assert(env);
@@ -656,7 +652,8 @@ crypto_pk_read_private_key_from_filename(crypto_pk_t *env,
return 0;
}
-/** Helper function to implement crypto_pk_write_*_key_to_string. */
+/** Helper function to implement crypto_pk_write_*_key_to_string. Return 0 on
+ * success, -1 on failure. */
static int
crypto_pk_write_key_to_string_impl(crypto_pk_t *env, char **dest,
size_t *len, int is_public)
@@ -897,7 +894,8 @@ crypto_pk_dup_key(crypto_pk_t *env)
return env;
}
-/** Make a real honest-to-goodness copy of <b>env</b>, and return it. */
+/** Make a real honest-to-goodness copy of <b>env</b>, and return it.
+ * Returns NULL on failure. */
crypto_pk_t *
crypto_pk_copy_full(crypto_pk_t *env)
{
@@ -1189,7 +1187,8 @@ crypto_pk_public_hybrid_encrypt(crypto_pk_t *env,
return -1;
}
-/** Invert crypto_pk_public_hybrid_encrypt. */
+/** Invert crypto_pk_public_hybrid_encrypt. Returns the number of bytes
+ * written on success, -1 on failure. */
int
crypto_pk_private_hybrid_decrypt(crypto_pk_t *env,
char *to,
@@ -1332,7 +1331,7 @@ crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out)
}
/** Copy <b>in</b> to the <b>outlen</b>-byte buffer <b>out</b>, adding spaces
- * every four spaces. */
+ * every four characters. */
void
crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in)
{
@@ -1484,7 +1483,7 @@ crypto_cipher_get_key(crypto_cipher_t *env)
/** 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.
+ * Does not check for failure.
*/
int
crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
@@ -1503,7 +1502,7 @@ crypto_cipher_encrypt(crypto_cipher_t *env, char *to,
/** Decrypt <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.
+ * Does not check for failure.
*/
int
crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
@@ -1519,7 +1518,7 @@ crypto_cipher_decrypt(crypto_cipher_t *env, char *to,
}
/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
- * on success, return 0. On failure, return -1.
+ * on success, return 0. Does not check for failure.
*/
int
crypto_cipher_crypt_inplace(crypto_cipher_t *env, char *buf, size_t len)
@@ -1591,7 +1590,7 @@ crypto_cipher_decrypt_with_iv(const char *key,
/** Compute the SHA1 digest of the <b>len</b> bytes on data stored in
* <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>.
- * Return 0 on success, -1 on failure.
+ * Return 0 on success, 1 on failure.
*/
int
crypto_digest(char *digest, const char *m, size_t len)
@@ -1603,7 +1602,7 @@ crypto_digest(char *digest, const char *m, size_t len)
/** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
* using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result
- * into <b>digest</b>. Return 0 on success, -1 on failure. */
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
int
crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm)
@@ -1614,6 +1613,19 @@ crypto_digest256(char *digest, const char *m, size_t len,
return (SHA256((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
}
+/** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>,
+ * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result
+ * into <b>digest</b>. Return 0 on success, 1 on failure. */
+int
+crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm)
+{
+ tor_assert(m);
+ tor_assert(digest);
+ tor_assert(algorithm == DIGEST_SHA512);
+ return (SHA512((const unsigned char*)m,len,(unsigned char*)digest) == NULL);
+}
+
/** Set the digests_t in <b>ds_out</b> to contain every digest on the
* <b>len</b> bytes in <b>m</b> that we know how to compute. Return 0 on
* success, -1 on failure. */
@@ -1626,8 +1638,18 @@ crypto_digest_all(digests_t *ds_out, const char *m, size_t len)
if (crypto_digest(ds_out->d[DIGEST_SHA1], m, len) < 0)
return -1;
for (i = DIGEST_SHA256; i < N_DIGEST_ALGORITHMS; ++i) {
- if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
- return -1;
+ switch (i) {
+ case DIGEST_SHA256:
+ if (crypto_digest256(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ case DIGEST_SHA512:
+ if (crypto_digest512(ds_out->d[i], m, len, i) < 0)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
}
return 0;
}
@@ -1641,6 +1663,8 @@ crypto_digest_algorithm_get_name(digest_algorithm_t alg)
return "sha1";
case DIGEST_SHA256:
return "sha256";
+ case DIGEST_SHA512:
+ return "sha512";
default:
tor_fragile_assert();
return "??unknown_digest??";
@@ -1656,6 +1680,8 @@ crypto_digest_algorithm_parse_name(const char *name)
return DIGEST_SHA1;
else if (!strcmp(name, "sha256"))
return DIGEST_SHA256;
+ else if (!strcmp(name, "sha512"))
+ return DIGEST_SHA512;
else
return -1;
}
@@ -1665,6 +1691,7 @@ struct crypto_digest_t {
union {
SHA_CTX sha1; /**< state for SHA1 */
SHA256_CTX sha2; /**< state for SHA256 */
+ SHA512_CTX sha512; /**< state for SHA512 */
} d; /**< State for the digest we're using. Only one member of the
* union is usable, depending on the value of <b>algorithm</b>. */
digest_algorithm_bitfield_t algorithm : 8; /**< Which algorithm is in use? */
@@ -1695,6 +1722,19 @@ crypto_digest256_new(digest_algorithm_t algorithm)
return r;
}
+/** Allocate and return a new digest object to compute 512-bit digests
+ * using <b>algorithm</b>. */
+crypto_digest_t *
+crypto_digest512_new(digest_algorithm_t algorithm)
+{
+ crypto_digest_t *r;
+ tor_assert(algorithm == DIGEST_SHA512);
+ r = tor_malloc(sizeof(crypto_digest_t));
+ SHA512_Init(&r->d.sha512);
+ r->algorithm = algorithm;
+ return r;
+}
+
/** Deallocate a digest object.
*/
void
@@ -1726,6 +1766,9 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
case DIGEST_SHA256:
SHA256_Update(&digest->d.sha2, (void*)data, len);
break;
+ case DIGEST_SHA512:
+ SHA512_Update(&digest->d.sha512, (void*)data, len);
+ break;
default:
tor_fragile_assert();
break;
@@ -1734,13 +1777,13 @@ crypto_digest_add_bytes(crypto_digest_t *digest, const char *data,
/** Compute the hash of the data that has been passed to the digest
* object; write the first out_len bytes of the result to <b>out</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN.
+ * <b>out_len</b> must be \<= DIGEST512_LEN.
*/
void
crypto_digest_get_digest(crypto_digest_t *digest,
char *out, size_t out_len)
{
- unsigned char r[DIGEST256_LEN];
+ unsigned char r[DIGEST512_LEN];
crypto_digest_t tmpenv;
tor_assert(digest);
tor_assert(out);
@@ -1755,6 +1798,10 @@ crypto_digest_get_digest(crypto_digest_t *digest,
tor_assert(out_len <= DIGEST256_LEN);
SHA256_Final(r, &tmpenv.d.sha2);
break;
+ case DIGEST_SHA512:
+ tor_assert(out_len <= DIGEST512_LEN);
+ SHA512_Final(r, &tmpenv.d.sha512);
+ break;
default:
log_warn(LD_BUG, "Called with unknown algorithm %d", digest->algorithm);
/* If fragile_assert is not enabled, then we should at least not
@@ -1796,7 +1843,7 @@ crypto_digest_assign(crypto_digest_t *into,
* at <b>digest_out</b> to the hash of the concatenation of those strings,
* plus the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>out_len</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist(char *digest_out, size_t len_out,
const smartlist_t *lst,
@@ -1811,7 +1858,7 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
* optional string <b>prepend</b>, those strings,
* and the optional string <b>append</b>, computed with the algorithm
* <b>alg</b>.
- * <b>out_len</b> must be \<= DIGEST256_LEN. */
+ * <b>len_out</b> must be \<= DIGEST512_LEN. */
void
crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *prepend,
@@ -1819,11 +1866,25 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
const char *append,
digest_algorithm_t alg)
{
- crypto_digest_t *d;
- if (alg == DIGEST_SHA1)
- d = crypto_digest_new();
- else
- d = crypto_digest256_new(alg);
+ crypto_digest_t *d = NULL;
+ switch (alg) {
+ case DIGEST_SHA1:
+ d = crypto_digest_new();
+ break;
+ case DIGEST_SHA256:
+ d = crypto_digest256_new(alg);
+ break;
+ case DIGEST_SHA512:
+ d = crypto_digest512_new(alg);
+ break;
+ default:
+ log_warn(LD_BUG, "Called with unknown algorithm %d", alg);
+ /* If fragile_assert is not enabled, wipe output and return
+ * without running any calculations */
+ memwipe(digest_out, 0xff, len_out);
+ tor_fragile_assert();
+ goto free;
+ }
if (prepend)
crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
@@ -1831,23 +1892,28 @@ crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
if (append)
crypto_digest_add_bytes(d, append, strlen(append));
crypto_digest_get_digest(d, digest_out, len_out);
+
+ free:
crypto_digest_free(d);
}
/** 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 DIGEST256_LEN-byte
- * result in <b>hmac_out</b>.
+ * result in <b>hmac_out</b>. Asserts on failure.
*/
void
crypto_hmac_sha256(char *hmac_out,
const char *key, size_t key_len,
const char *msg, size_t msg_len)
{
+ unsigned char *rv = NULL;
/* 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);
- HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
- (unsigned char*)hmac_out, NULL);
+ tor_assert(hmac_out);
+ rv = HMAC(EVP_sha256(), key, (int)key_len, (unsigned char*)msg, (int)msg_len,
+ (unsigned char*)hmac_out, NULL);
+ tor_assert(rv);
}
/* DH */
@@ -1941,7 +2007,8 @@ init_dh_param(void)
*/
#define DH_PRIVATE_KEY_BITS 320
-/** Allocate and return a new DH object for a key exchange.
+/** Allocate and return a new DH object for a key exchange. Returns NULL on
+ * failure.
*/
crypto_dh_t *
crypto_dh_new(int dh_type)
@@ -2164,7 +2231,7 @@ int
crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
uint8_t *key_out, size_t key_out_len)
{
- int i;
+ int i, r = -1;
uint8_t *cp, *tmp = tor_malloc(key_in_len+1);
uint8_t digest[DIGEST_LEN];
@@ -2176,19 +2243,16 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
++i, cp += DIGEST_LEN) {
tmp[key_in_len] = i;
if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1))
- goto err;
+ goto exit;
memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out)));
}
- memwipe(tmp, 0, key_in_len+1);
- tor_free(tmp);
- memwipe(digest, 0, sizeof(digest));
- return 0;
- err:
+ r = 0;
+ exit:
memwipe(tmp, 0, key_in_len+1);
tor_free(tmp);
memwipe(digest, 0, sizeof(digest));
- return -1;
+ return r;
}
/** Expand some secret key material according to RFC5869, using SHA256 as the
@@ -2196,7 +2260,7 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len,
* secret key material; the <b>salt_in_len</b> bytes at <b>salt_in</b> and the
* <b>info_in_len</b> bytes in <b>info_in_len</b> are the algorithm's "salt"
* and "info" parameters respectively. On success, write <b>key_out_len</b>
- * bytes to <b>key_out</b> and return 0. On failure, return -1.
+ * bytes to <b>key_out</b> and return 0. Assert on failure.
*/
int
crypto_expand_key_material_rfc5869_sha256(
@@ -2280,7 +2344,7 @@ crypto_seed_weak_rng(tor_weak_rng_t *rng)
}
/** Try to get <b>out_len</b> bytes of the strongest entropy we can generate,
- * storing it into <b>out</b>.
+ * storing it into <b>out</b>. Return -1 on success, 0 on failure.
*/
int
crypto_strongest_rand(uint8_t *out, size_t out_len)
@@ -2335,8 +2399,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
}
/** Seed OpenSSL's random number generator with bytes from the operating
- * system. <b>startup</b> should be true iff we have just started Tor and
- * have not yet allocated a bunch of fds. Return 0 on success, -1 on failure.
+ * system. Return 0 on success, -1 on failure.
*/
int
crypto_seed_rng(void)
@@ -2358,34 +2421,41 @@ crypto_seed_rng(void)
memwipe(buf, 0, sizeof(buf));
- if (rand_poll_ok || load_entropy_ok)
+ if ((rand_poll_ok || load_entropy_ok) && RAND_status() == 1)
return 0;
else
return -1;
}
-/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
- * success, -1 on failure, with support for mocking for unit tests.
+/** Write <b>n</b> bytes of strong random data to <b>to</b>. Supports mocking
+ * for unit tests.
+ *
+ * This function is not allowed to fail; if it would fail to generate strong
+ * entropy, it must terminate the process instead.
*/
-MOCK_IMPL(int,
+MOCK_IMPL(void,
crypto_rand, (char *to, size_t n))
{
- return crypto_rand_unmocked(to, n);
+ crypto_rand_unmocked(to, n);
}
-/** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
- * success, -1 on failure. Most callers will want crypto_rand instead.
+/** Write <b>n</b> bytes of strong random data to <b>to</b>. Most callers
+ * will want crypto_rand instead.
+ *
+ * This function is not allowed to fail; if it would fail to generate strong
+ * entropy, it must terminate the process instead.
*/
-int
+void
crypto_rand_unmocked(char *to, size_t n)
{
int r;
+ if (n == 0)
+ return;
+
tor_assert(n < INT_MAX);
tor_assert(to);
r = RAND_bytes((unsigned char*)to, (int)n);
- if (r == 0)
- crypto_log_errors(LOG_WARN, "generating random data");
- return (r == 1) ? 0 : -1;
+ tor_assert(r >= 0);
}
/** Return a pseudorandom integer, chosen uniformly from the values
@@ -2411,8 +2481,8 @@ crypto_rand_int(unsigned int max)
}
}
-/** Return a pseudorandom integer, chosen uniformly from the values <i>i</i>
- * such that <b>min</b> &lt;= <i>i</i> &lt <b>max</b>.
+/** Return a pseudorandom integer, chosen uniformly from the values i such
+ * that min <= i < max.
*
* <b>min</b> MUST be in range [0, <b>max</b>).
* <b>max</b> MUST be in range (min, INT_MAX].
@@ -2489,7 +2559,7 @@ crypto_rand_double(void)
/** Generate and return a new random hostname starting with <b>prefix</b>,
* 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. Does not check for failure.
*
* Clip <b>max_rand_len</b> to MAX_DNS_LABEL_SIZE.
**/
@@ -2671,7 +2741,7 @@ tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
/** @{ */
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
- * multithreaded. */
+ * multithreaded. Returns 0. */
static int
setup_openssl_threading(void)
{
@@ -2689,17 +2759,14 @@ setup_openssl_threading(void)
return 0;
}
-/** Uninitialize the crypto library. Return 0 on success, -1 on failure.
+/** Uninitialize the crypto library. Return 0 on success. Does not detect
+ * failure.
*/
int
crypto_global_cleanup(void)
{
EVP_cleanup();
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
ERR_free_strings();
if (dh_param_p)
diff --git a/src/common/crypto.h b/src/common/crypto.h
index 6256f7346b..9b922ff818 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -16,6 +16,7 @@
#include <stdio.h>
#include "torint.h"
#include "testsupport.h"
+#include "compat.h"
/*
Macro to create an arbitrary OpenSSL version number as used by
@@ -54,6 +55,8 @@
/** Length of the output of our second (improved) message digests. (For now
* this is just sha256, but it could be any other 256-bit digest.) */
#define DIGEST256_LEN 32
+/** Length of the output of our 64-bit optimized message digests (SHA512). */
+#define DIGEST512_LEN 64
/** Length of our symmetric cipher's keys. */
#define CIPHER_KEY_LEN 16
/** Length of our symmetric cipher's IV. */
@@ -69,6 +72,9 @@
/** Length of a sha256 message digest when encoded in base64 with trailing =
* signs removed. */
#define BASE64_DIGEST256_LEN 43
+/** Length of a sha512 message digest when encoded in base64 with trailing =
+ * signs removed. */
+#define BASE64_DIGEST512_LEN 86
/** Constant used to indicate OAEP padding for public-key encryption */
#define PK_PKCS1_OAEP_PADDING 60002
@@ -83,24 +89,27 @@
#define HEX_DIGEST_LEN 40
/** Length of hex encoding of SHA256 digest, not including final NUL. */
#define HEX_DIGEST256_LEN 64
+/** Length of hex encoding of SHA512 digest, not including final NUL. */
+#define HEX_DIGEST512_LEN 128
typedef enum {
DIGEST_SHA1 = 0,
DIGEST_SHA256 = 1,
+ DIGEST_SHA512 = 2,
} digest_algorithm_t;
-#define N_DIGEST_ALGORITHMS (DIGEST_SHA256+1)
+#define N_DIGEST_ALGORITHMS (DIGEST_SHA512+1)
#define digest_algorithm_bitfield_t ENUM_BF(digest_algorithm_t)
/** A set of all the digests we know how to compute, taken on a single
- * string. Any digests that are shorter than 256 bits are right-padded
+ * string. Any digests that are shorter than 512 bits are right-padded
* with 0 bits.
*
- * Note that this representation wastes 12 bytes for the SHA1 case, so
+ * Note that this representation wastes 44 bytes for the SHA1 case, so
* don't use it for anything where we need to allocate a whole bunch at
* once.
**/
typedef struct {
- char d[N_DIGEST_ALGORITHMS][DIGEST256_LEN];
+ char d[N_DIGEST_ALGORITHMS][DIGEST512_LEN];
} digests_t;
typedef struct crypto_pk_t crypto_pk_t;
@@ -111,15 +120,15 @@ typedef struct crypto_dh_t crypto_dh_t;
/* global state */
const char * crypto_openssl_get_version_str(void);
const char * crypto_openssl_get_header_version_str(void);
-int crypto_early_init(void);
+int crypto_early_init(void) ATTR_WUR;
int crypto_global_init(int hardwareAccel,
const char *accelName,
- const char *accelPath);
+ const char *accelPath) ATTR_WUR;
void crypto_thread_cleanup(void);
int crypto_global_cleanup(void);
/* environment setup */
-crypto_pk_t *crypto_pk_new(void);
+MOCK_DECL(crypto_pk_t *,crypto_pk_new,(void));
void crypto_pk_free(crypto_pk_t *env);
void crypto_set_tls_dh_prime(void);
@@ -128,7 +137,7 @@ 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_t *env, int bits);
+MOCK_DECL(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))
@@ -207,6 +216,8 @@ int crypto_cipher_decrypt_with_iv(const char *key,
int crypto_digest(char *digest, const char *m, size_t len);
int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
+int crypto_digest512(char *digest, const char *m, size_t len,
+ digest_algorithm_t algorithm);
int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
struct smartlist_t;
void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
@@ -221,6 +232,7 @@ const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
int crypto_digest_algorithm_parse_name(const char *name);
crypto_digest_t *crypto_digest_new(void);
crypto_digest_t *crypto_digest256_new(digest_algorithm_t algorithm);
+crypto_digest_t *crypto_digest512_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);
@@ -258,9 +270,9 @@ int crypto_expand_key_material_rfc5869_sha256(
uint8_t *key_out, size_t key_out_len);
/* random numbers */
-int crypto_seed_rng(void);
-MOCK_DECL(int,crypto_rand,(char *to, size_t n));
-int crypto_rand_unmocked(char *to, size_t n);
+int crypto_seed_rng(void) ATTR_WUR;
+MOCK_DECL(void,crypto_rand,(char *to, size_t n));
+void crypto_rand_unmocked(char *to, size_t n);
int crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
int crypto_rand_int_range(unsigned int min, unsigned int max);
@@ -289,8 +301,8 @@ struct evp_pkey_st;
struct dh_st;
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);
+MOCK_DECL(struct evp_pkey_st *, crypto_pk_get_evp_pkey_,(crypto_pk_t *env,
+ int private));
struct dh_st *crypto_dh_get_dh_(crypto_dh_t *dh);
void crypto_add_spaces_to_fp(char *out, size_t outlen, const char *in);
diff --git a/src/common/crypto_curve25519.c b/src/common/crypto_curve25519.c
index ac0b08a552..00302a2ff0 100644
--- a/src/common/crypto_curve25519.c
+++ b/src/common/crypto_curve25519.c
@@ -113,8 +113,7 @@ curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong)
{
uint8_t k_tmp[CURVE25519_SECKEY_LEN];
- if (crypto_rand((char*)out, CURVE25519_SECKEY_LEN) < 0)
- return -1;
+ crypto_rand((char*)out, CURVE25519_SECKEY_LEN);
if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) {
/* If they asked for extra-strong entropy and we have some, use it as an
* HMAC key to improve not-so-good entropy rather than using it directly,
diff --git a/src/common/include.am b/src/common/include.am
index 7de93ba2ac..2fc92e2ceb 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -118,6 +118,7 @@ COMMONHEADERS = \
src/common/ciphers.inc \
src/common/compat.h \
src/common/compat_libevent.h \
+ src/common/compat_openssl.h \
src/common/compat_threads.h \
src/common/container.h \
src/common/crypto.h \
diff --git a/src/common/log.c b/src/common/log.c
index e23691b6ab..7ede6100a2 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -1097,14 +1097,25 @@ add_file_log(const log_severity_list_t *severity, const char *filename,
#ifdef HAVE_SYSLOG_H
/**
* Add a log handler to send messages to they system log facility.
+ *
+ * If this is the first log handler, opens syslog with ident Tor or
+ * Tor-<syslog_identity_tag> if that is not NULL.
*/
int
-add_syslog_log(const log_severity_list_t *severity)
+add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag)
{
logfile_t *lf;
- if (syslog_count++ == 0)
+ if (syslog_count++ == 0) {
/* This is the first syslog. */
- openlog("Tor", LOG_PID | LOG_NDELAY, LOGFACILITY);
+ static char buf[256];
+ if (syslog_identity_tag) {
+ tor_snprintf(buf, sizeof(buf), "Tor-%s", syslog_identity_tag);
+ } else {
+ tor_snprintf(buf, sizeof(buf), "Tor");
+ }
+ openlog(buf, LOG_PID | LOG_NDELAY, LOGFACILITY);
+ }
lf = tor_malloc_zero(sizeof(logfile_t));
lf->fd = -1;
diff --git a/src/common/procmon.c b/src/common/procmon.c
index 2d0f021724..346a0c6943 100644
--- a/src/common/procmon.c
+++ b/src/common/procmon.c
@@ -192,7 +192,8 @@ tor_process_monitor_new(struct event_base *base,
tor_procmon_callback_t cb, void *cb_arg,
const char **msg)
{
- tor_process_monitor_t *procmon = tor_malloc(sizeof(tor_process_monitor_t));
+ tor_process_monitor_t *procmon = tor_malloc_zero(
+ sizeof(tor_process_monitor_t));
struct parsed_process_specifier_t ppspec;
tor_assert(msg != NULL);
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 67edf14c04..3e8667895f 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -135,7 +135,8 @@ void add_stream_log(const log_severity_list_t *severity, const char *name,
int add_file_log(const log_severity_list_t *severity, const char *filename,
const int truncate);
#ifdef HAVE_SYSLOG_H
-int add_syslog_log(const log_severity_list_t *severity);
+int add_syslog_log(const log_severity_list_t *severity,
+ const char* syslog_identity_tag);
#endif
int add_callback_log(const log_severity_list_t *severity, log_callback cb);
void logs_set_domain_logging(int enabled);
@@ -183,25 +184,25 @@ void log_fn_ratelim_(struct ratelim_t *ratelim, int severity,
/** Log a message at level <b>severity</b>, using a pretty-printed version
* of the current function name. */
#define log_fn(severity, domain, args...) \
- log_fn_(severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(severity, domain, __FUNCTION__, args)
/** As log_fn, but use <b>ratelim</b> (an instance of ratelim_t) to control
* the frequency at which messages can appear.
*/
#define log_fn_ratelim(ratelim, severity, domain, args...) \
- log_fn_ratelim_(ratelim, severity, domain, __PRETTY_FUNCTION__, args)
+ log_fn_ratelim_(ratelim, severity, domain, __FUNCTION__, args)
#define log_debug(domain, args...) \
STMT_BEGIN \
if (PREDICT_UNLIKELY(log_global_min_severity_ == LOG_DEBUG)) \
- log_fn_(LOG_DEBUG, domain, __PRETTY_FUNCTION__, args); \
+ log_fn_(LOG_DEBUG, domain, __FUNCTION__, args); \
STMT_END
#define log_info(domain, args...) \
- log_fn_(LOG_INFO, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_INFO, domain, __FUNCTION__, args)
#define log_notice(domain, args...) \
- log_fn_(LOG_NOTICE, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_NOTICE, domain, __FUNCTION__, args)
#define log_warn(domain, args...) \
- log_fn_(LOG_WARN, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_WARN, domain, __FUNCTION__, args)
#define log_err(domain, args...) \
- log_fn_(LOG_ERR, domain, __PRETTY_FUNCTION__, args)
+ log_fn_(LOG_ERR, domain, __FUNCTION__, args)
#else /* ! defined(__GNUC__) */
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 536043e558..79c6998806 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -16,6 +16,8 @@
#include "orconfig.h"
+#define TORTLS_PRIVATE
+
#include <assert.h>
#ifdef _WIN32 /*wrkard for dtls1.h >= 0.9.8m of "#include <winsock.h>"*/
#include <winsock2.h>
@@ -38,9 +40,6 @@
#include <openssl/opensslv.h>
#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
-#error "We require OpenSSL >= 1.0.0"
-#endif
#ifdef OPENSSL_NO_EC
#error "We require OpenSSL with ECC support"
#endif
@@ -69,6 +68,7 @@
#include "compat_libevent.h"
#endif
+#define TORTLS_PRIVATE
#include "tortls.h"
#include "util.h"
#include "torlog.h"
@@ -80,11 +80,6 @@
#define X509_get_notAfter_const(cert) \
((const ASN1_TIME*) X509_get_notAfter((X509 *)cert))
-/* Enable the "v2" TLS handshake.
- */
-#define V2_HANDSHAKE_SERVER
-#define V2_HANDSHAKE_CLIENT
-
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
@@ -113,29 +108,6 @@
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
#endif
-/** Structure that we use for a single certificate. */
-struct tor_x509_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;
- tor_x509_cert_t *my_link_cert;
- tor_x509_cert_t *my_id_cert;
- tor_x509_cert_t *my_auth_cert;
- crypto_pk_t *link_key;
- crypto_pk_t *auth_key;
-} tor_tls_context_t;
-
/** Return values for tor_tls_classify_client_ciphers.
*
* @{
@@ -154,60 +126,12 @@ typedef struct tor_tls_context_t {
#define CIPHERS_UNRESTRICTED 3
/** @} */
-#define TOR_TLS_MAGIC 0x71571571
-
-typedef 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
-} tor_tls_state_t;
-#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
-
-/** Holds a SSL object and its associated data. Members are only
- * accessed from within tortls.c.
- */
-struct tor_tls_t {
- 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. */
- char *address; /**< An address to log when describing this connection. */
- tor_tls_state_bitfield_t 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 */
- unsigned int wasV2Handshake:1; /**< True iff the original handshake for
- * this connection used the updated version
- * 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. */
- unsigned int got_renegotiate:1;
- /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
- * called that function yet. */
- int8_t client_cipher_list_type;
- /** 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
- * tor_tls_get_n_raw_bytes() for usage.
- */
- unsigned long last_write_count;
- unsigned long last_read_count;
- /** If set, a callback to invoke whenever the client tries to renegotiate
- * the handshake. */
- void (*negotiated_callback)(tor_tls_t *tls, void *arg);
- /** Argument to pass to negotiated_callback. */
- void *callback_arg;
-};
-
/** 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;
+STATIC int tor_tls_object_ex_data_index = -1;
/** Helper: Allocate tor_tls_object_ex_data_index. */
-static void
+STATIC void
tor_tls_allocate_tor_tls_object_ex_data_index(void)
{
if (tor_tls_object_ex_data_index == -1) {
@@ -219,7 +143,7 @@ tor_tls_allocate_tor_tls_object_ex_data_index(void)
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
-static INLINE tor_tls_t *
+STATIC INLINE tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
tor_tls_t *result = SSL_get_ex_data(ssl, tor_tls_object_ex_data_index);
@@ -230,21 +154,7 @@ tor_tls_get_by_ssl(const SSL *ssl)
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_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime);
-
-static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
- crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
-static tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
- unsigned int key_lifetime,
- unsigned int flags,
- int is_client);
+
static int check_cert_lifetime_internal(int severity, const X509 *cert,
int past_tolerance, int future_tolerance);
@@ -252,8 +162,8 @@ static int check_cert_lifetime_internal(int severity, const X509 *cert,
* to touch them.
*
* @{ */
-static tor_tls_context_t *server_tls_context = NULL;
-static tor_tls_context_t *client_tls_context = NULL;
+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. */
@@ -347,7 +257,7 @@ tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
/** Log all pending tls errors at level <b>severity</b> in log domain
* <b>domain</b>. Use <b>doing</b> to describe our current activities.
*/
-static void
+STATIC void
tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
{
unsigned long err;
@@ -359,7 +269,7 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing)
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
-static int
+STATIC int
tor_errno_to_tls_error(int e)
{
switch (e) {
@@ -410,7 +320,7 @@ tor_tls_err_to_string(int err)
* If an error has occurred, log it at level <b>severity</b> and describe the
* current action as <b>doing</b>.
*/
-static int
+STATIC int
tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity, int domain)
{
@@ -466,8 +376,9 @@ tor_tls_init(void)
#if (SIZEOF_VOID_P >= 8 && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
- long version = SSLeay();
+ long version = OpenSSL_version_num();
+ /* LCOV_EXCL_START : we can't test these lines on the same machine */
if (version >= OPENSSL_V_SERIES(1,0,1)) {
/* Warn if we could *almost* be running with much faster ECDH.
If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
@@ -494,6 +405,7 @@ tor_tls_init(void)
"support (using the enable-ec_nistp_64_gcc_128 option "
"when configuring it) would make ECDH much faster.");
}
+ /* LCOV_EXCL_STOP */
#endif
tor_tls_allocate_tor_tls_object_ex_data_index();
@@ -524,7 +436,7 @@ tor_tls_free_all(void)
* it: We always accept peer certs and complete the handshake. We
* don't validate them until later.
*/
-static int
+STATIC int
always_accept_verify_cb(int preverify_ok,
X509_STORE_CTX *x509_ctx)
{
@@ -539,16 +451,20 @@ tor_x509_name_new(const char *cname)
{
int nid;
X509_NAME *name;
+ /* LCOV_EXCL_BR_START : these branches will only fail on OOM errors */
if (!(name = X509_NAME_new()))
return NULL;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
(unsigned char*)cname, -1, -1, 0)))
goto error;
+ /* LCOV_EXCL_BR_STOP */
return name;
error:
+ /* LCOV_EXCL_START : these lines will only execute on out of memory errors*/
X509_NAME_free(name);
return NULL;
+ /* LCOV_EXCL_STOP */
}
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
@@ -559,12 +475,12 @@ tor_x509_name_new(const char *cname)
*
* Return a certificate on success, NULL on failure.
*/
-static X509 *
-tor_tls_create_certificate(crypto_pk_t *rsa,
- crypto_pk_t *rsa_sign,
- const char *cname,
- const char *cname_sign,
- unsigned int cert_lifetime)
+MOCK_IMPL(STATIC X509 *,
+ 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. */
@@ -601,8 +517,7 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
goto error;
{ /* our serial number is 8 random bytes. */
- if (crypto_rand((char *)serial_tmp, sizeof(serial_tmp)) < 0)
- goto error;
+ crypto_rand((char *)serial_tmp, sizeof(serial_tmp));
if (!(serial_number = BN_bin2bn(serial_tmp, sizeof(serial_tmp), NULL)))
goto error;
if (!(BN_to_ASN1_INTEGER(serial_number, X509_get_serialNumber(x509))))
@@ -731,7 +646,9 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
X509_free(cert->cert);
tor_free(cert->encoded);
memwipe(cert, 0x03, sizeof(*cert));
+ /* LCOV_EXCL_BR_START since cert will never be NULL here */
tor_free(cert);
+ /* LCOV_EXCL_BR_STOP */
}
/**
@@ -739,8 +656,8 @@ tor_x509_cert_free(tor_x509_cert_t *cert)
*
* Steals a reference to x509_cert.
*/
-static tor_x509_cert_t *
-tor_x509_cert_new(X509 *x509_cert)
+MOCK_IMPL(STATIC tor_x509_cert_t *,
+ tor_x509_cert_new,(X509 *x509_cert))
{
tor_x509_cert_t *cert;
EVP_PKEY *pkey;
@@ -754,10 +671,12 @@ tor_x509_cert_new(X509 *x509_cert)
length = i2d_X509(x509_cert, &buf);
cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
if (length <= 0 || buf == NULL) {
+ /* LCOV_EXCL_START for the same reason as the exclusion above */
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
X509_free(x509_cert);
return NULL;
+ /* LCOV_EXCL_STOP */
}
cert->encoded_len = (size_t) length;
cert->encoded = tor_malloc(length);
@@ -864,7 +783,9 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_x509_cert_free(ctx->my_auth_cert);
crypto_pk_free(ctx->link_key);
crypto_pk_free(ctx->auth_key);
+ /* LCOV_EXCL_BR_START since ctx will never be NULL here */
tor_free(ctx);
+ /* LCOV_EXCL_BR_STOP */
}
}
@@ -960,11 +881,13 @@ tor_tls_cert_is_valid(int severity,
int check_rsa_1024)
{
check_no_tls_errors();
-
EVP_PKEY *cert_key;
- EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
int r, key_ok = 0;
+ if (!signing_cert)
+ goto bad;
+
+ EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert);
if (!signing_key)
goto bad;
r = X509_verify(cert->cert, signing_key);
@@ -1085,7 +1008,7 @@ tor_tls_context_init(unsigned flags,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
-static int
+STATIC int
tor_tls_context_init_one(tor_tls_context_t **ppcontext,
crypto_pk_t *identity,
unsigned int key_lifetime,
@@ -1119,7 +1042,7 @@ tor_tls_context_init_one(tor_tls_context_t **ppcontext,
* <b>identity</b> should be set to the identity key used to sign the
* certificate.
*/
-static tor_tls_context_t *
+STATIC tor_tls_context_t *
tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
unsigned flags, int is_client)
{
@@ -1200,23 +1123,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
* historically been chosen for fingerprinting resistance. */
SSL_CTX_set_options(result->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
- /* Disable TLS1.1 and TLS1.2 if they exist. We need to do this to
- * workaround a bug present in all OpenSSL 1.0.1 versions (as of 1
- * June 2012), wherein renegotiating while using one of these TLS
- * protocols will cause the client to send a TLS 1.0 ServerHello
- * rather than a ServerHello written with the appropriate protocol
- * version. Once some version of OpenSSL does TLS1.1 and TLS1.2
- * renegotiation properly, we can turn them back on when built with
- * that version. */
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,1,'e')
-#ifdef SSL_OP_NO_TLSv1_2
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_2);
-#endif
-#ifdef SSL_OP_NO_TLSv1_1
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_TLSv1_1);
-#endif
-#endif
-
/* Disable TLS tickets if they're supported. We never want to use them;
* using them can make our perfect forward secrecy a little worse, *and*
* create an opportunity to fingerprint us (since it's unusual to use them
@@ -1343,11 +1249,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
/** Invoked when a TLS state changes: log the change at severity 'debug' */
-static void
+STATIC void
tor_tls_debug_state_callback(const SSL *ssl, int type, int val)
{
+ /* LCOV_EXCL_START since this depends on whether debug is captured or not */
log_debug(LD_HANDSHAKE, "SSL %p is now in state %s [type=%d,val=%d].",
ssl, SSL_state_string_long(ssl), type, val);
+ /* LCOV_EXCL_STOP */
}
/* Return the name of the negotiated ciphersuite in use on <b>tls</b> */
@@ -1357,13 +1265,11 @@ tor_tls_get_ciphersuite_name(tor_tls_t *tls)
return SSL_get_cipher(tls->ssl);
}
-#ifdef V2_HANDSHAKE_SERVER
-
/* Here's the old V2 cipher list we sent from 0.2.1.1-alpha up to
* 0.2.3.17-beta. If a client is using this list, we can't believe the ciphers
* that it claims to support. We'll prune this list to remove the ciphers
* *we* don't recognize. */
-static uint16_t v2_cipher_list[] = {
+STATIC uint16_t v2_cipher_list[] = {
0xc00a, /* TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA */
0xc014, /* TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA */
0x0039, /* TLS1_TXT_DHE_RSA_WITH_AES_256_SHA */
@@ -1399,7 +1305,7 @@ static int v2_cipher_list_pruned = 0;
/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
* return 1 if it does support it, or if we have no way to tell. */
-static int
+STATIC int
find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{
const SSL_CIPHER *c;
@@ -1481,7 +1387,7 @@ prune_v2_cipher_list(const SSL *ssl)
* client it is. Return one of CIPHERS_ERR, CIPHERS_V1, CIPHERS_V2,
* CIPHERS_UNRESTRICTED.
**/
-static int
+STATIC int
tor_tls_classify_client_ciphers(const SSL *ssl,
STACK_OF(SSL_CIPHER) *peer_ciphers)
{
@@ -1563,7 +1469,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
-static int
+STATIC int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
STACK_OF(SSL_CIPHER) *ciphers;
@@ -1587,11 +1493,10 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
* do not send or request extra certificates in v2 handshakes.</li>
* <li>To detect renegotiation</li></ul>
*/
-static void
+STATIC void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
- int ssl_state;
(void) val;
tor_tls_debug_state_callback(ssl, type, val);
@@ -1599,11 +1504,9 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (type != SSL_CB_ACCEPT_LOOP)
return;
- ssl_state = SSL_state(ssl);
- if ((ssl_state != SSL3_ST_SW_SRVR_HELLO_A) &&
- (ssl_state != SSL3_ST_SW_SRVR_HELLO_B))
+ OSSL_HANDSHAKE_STATE ssl_state = SSL_get_state(ssl);
+ if (! STATE_IS_SW_SERVER_HELLO(ssl_state))
return;
-
tls = tor_tls_get_by_ssl(ssl);
if (tls) {
/* Check whether we're watching for renegotiates. If so, this is one! */
@@ -1633,11 +1536,12 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
} else {
+ /* LCOV_EXCL_START this line is not reachable */
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
+ /* LCOV_EXCL_STOP */
}
}
}
-#endif
/** Callback to get invoked on a server after we've read the list of ciphers
* the client supports, but before we pick our own ciphersuite.
@@ -1651,7 +1555,7 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
* authentication on the fly. But as long as we return 0, we won't actually be
* setting up a shared secret, and all will be fine.
*/
-static int
+STATIC int
tor_tls_session_secret_cb(SSL *ssl, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
SSL_CIPHER **cipher, void *arg)
@@ -1747,12 +1651,9 @@ tor_tls_new(int sock, int isServer)
log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
result->last_read_count, result->last_write_count);
}
-#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
- } else
-#endif
- {
+ } else {
SSL_set_info_callback(result->ssl, tor_tls_debug_state_callback);
}
@@ -1791,13 +1692,11 @@ tor_tls_set_renegotiate_callback(tor_tls_t *tls,
tls->negotiated_callback = cb;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
-#ifdef V2_HANDSHAKE_SERVER
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
SSL_set_info_callback(tls->ssl, tor_tls_debug_state_callback);
}
-#endif
}
/** If this version of openssl requires it, turn on renegotiation on
@@ -1884,7 +1783,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
if (r > 0) {
-#ifdef V2_HANDSHAKE_SERVER
if (tls->got_renegotiate) {
/* Renegotiation happened! */
log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
@@ -1892,7 +1790,6 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
tls->negotiated_callback(tls, tls->callback_arg);
tls->got_renegotiate = 0;
}
-#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG, LD_NET);
@@ -1909,10 +1806,10 @@ tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
/** Total number of bytes that we've used TLS to send. Used to track TLS
* overhead. */
-static uint64_t total_bytes_written_over_tls = 0;
+STATIC uint64_t total_bytes_written_over_tls = 0;
/** Total number of bytes that TLS has put on the network for us. Used to
* track TLS overhead. */
-static uint64_t total_bytes_written_by_tls = 0;
+STATIC uint64_t total_bytes_written_by_tls = 0;
/** Underlying function for TLS writing. Write up to <b>n</b>
* characters from <b>cp</b> onto <b>tls</b>. On success, returns the
@@ -1957,12 +1854,14 @@ int
tor_tls_handshake(tor_tls_t *tls)
{
int r;
- int oldstate;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
+
check_no_tls_errors();
- oldstate = SSL_state(tls->ssl);
+
+ OSSL_HANDSHAKE_STATE oldstate = SSL_get_state(tls->ssl);
+
if (tls->isServer) {
log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
SSL_state_string_long(tls->ssl));
@@ -1972,7 +1871,10 @@ tor_tls_handshake(tor_tls_t *tls)
SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
- if (oldstate != SSL_state(tls->ssl))
+
+ OSSL_HANDSHAKE_STATE newstate = SSL_get_state(tls->ssl);
+
+ if (oldstate != newstate)
log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
tls, SSL_state_string_long(tls->ssl));
/* We need to call this here and not earlier, since OpenSSL has a penchant
@@ -2008,7 +1910,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
-#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
/* 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
@@ -2023,26 +1924,10 @@ tor_tls_finish_handshake(tor_tls_t *tls)
} 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);
-#endif
+ /* Client-side */
+ tls->wasV2Handshake = 1;
+ /* XXXX this can move, probably? -NM */
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;
@@ -2052,52 +1937,6 @@ tor_tls_finish_handshake(tor_tls_t *tls)
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.
- */
-int
-tor_tls_renegotiate(tor_tls_t *tls)
-{
- int r;
- tor_assert(tls);
- /* We could do server-initiated renegotiation too, but that would be tricky.
- * Instead of "SSL_renegotiate, then SSL_do_handshake until done" */
- tor_assert(!tls->isServer);
-
- check_no_tls_errors();
- if (tls->state != TOR_TLS_ST_RENEGOTIATE) {
- int r = SSL_renegotiate(tls->ssl);
- if (r <= 0) {
- return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN,
- LD_HANDSHAKE);
- }
- tls->state = TOR_TLS_ST_RENEGOTIATE;
- }
- r = SSL_do_handshake(tls->ssl);
- if (r == 1) {
- tls->state = TOR_TLS_ST_OPEN;
- return TOR_TLS_DONE;
- } else
- return tor_tls_get_error(tls, r, 0, "renegotiating handshake", LOG_INFO,
- LD_HANDSHAKE);
-}
-
/** Shut down an open tls connection <b>tls</b>. When finished, returns
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD,
* or TOR_TLS_WANTWRITE.
@@ -2251,15 +2090,14 @@ log_cert_lifetime(int severity, const X509 *cert, const char *problem)
*
* Note that a reference is added to cert_out, so it needs to be
* freed. id_cert_out doesn't. */
-static void
-try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
- X509 **cert_out, X509 **id_cert_out)
+MOCK_IMPL(STATIC void,
+try_to_extract_certs_from_tls,(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out))
{
X509 *cert = NULL, *id_cert = NULL;
STACK_OF(X509) *chain = NULL;
int num_in_chain, i;
*cert_out = *id_cert_out = NULL;
-
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
return;
*cert_out = cert;
@@ -2476,114 +2314,7 @@ check_no_tls_errors_(const char *fname, int line)
int
tor_tls_used_v1_handshake(tor_tls_t *tls)
{
-#if defined(V2_HANDSHAKE_SERVER) && defined(V2_HANDSHAKE_CLIENT)
return ! tls->wasV2Handshake;
-#else
- if (tls->isServer) {
-# ifdef V2_HANDSHAKE_SERVER
- return ! tls->wasV2Handshake;
-# endif
- } else {
-# ifdef V2_HANDSHAKE_CLIENT
- return ! tls->wasV2Handshake;
-# endif
- }
- return 1;
-#endif
-}
-
-/** 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;
- if (len < 4) {
- OPENSSL_free(s);
- return 1;
- }
- 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)
-{
- check_no_tls_errors();
-
- 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:
- tls_log_errors(tls, LOG_WARN, LD_NET, "checking for a v3 cert");
-
- 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
@@ -2629,7 +2360,7 @@ SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
#endif
#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
-static size_t
+STATIC size_t
SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
{
tor_assert(s);
@@ -2652,7 +2383,6 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
uint8_t buf[128];
size_t len;
-
tor_assert(tls);
SSL *const ssl = tls->ssl;
@@ -2676,12 +2406,14 @@ tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
size_t r = SSL_get_client_random(ssl, buf, client_random_len);
tor_assert(r == client_random_len);
}
+
{
size_t r = SSL_get_server_random(ssl,
buf+client_random_len,
server_random_len);
tor_assert(r == server_random_len);
}
+
uint8_t *master_key = tor_malloc_zero(master_key_len);
{
size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 124b77160f..6a4ef9aebe 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -12,6 +12,7 @@
**/
#include "crypto.h"
+#include "compat_openssl.h"
#include "compat.h"
#include "testsupport.h"
@@ -51,6 +52,119 @@ typedef struct tor_x509_cert_t tor_x509_cert_t;
case TOR_TLS_ERROR_IO
#define TOR_TLS_IS_ERROR(rv) ((rv) < TOR_TLS_CLOSE)
+
+#ifdef TORTLS_PRIVATE
+#define TOR_TLS_MAGIC 0x71571571
+
+typedef 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
+} tor_tls_state_t;
+#define tor_tls_state_bitfield_t ENUM_BF(tor_tls_state_t)
+
+/** Holds a SSL_CTX object and related state used to configure TLS
+ * connections.
+ */
+typedef struct tor_tls_context_t {
+ int refcnt;
+ SSL_CTX *ctx;
+ tor_x509_cert_t *my_link_cert;
+ tor_x509_cert_t *my_id_cert;
+ tor_x509_cert_t *my_auth_cert;
+ crypto_pk_t *link_key;
+ crypto_pk_t *auth_key;
+} tor_tls_context_t;
+
+/** Structure that we use for a single certificate. */
+struct tor_x509_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 object and its associated data. Members are only
+ * accessed from within tortls.c.
+ */
+struct tor_tls_t {
+ 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. */
+ char *address; /**< An address to log when describing this connection. */
+ tor_tls_state_bitfield_t 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 */
+ unsigned int wasV2Handshake:1; /**< True iff the original handshake for
+ * this connection used the updated version
+ * 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. */
+ unsigned int got_renegotiate:1;
+ /** Return value from tor_tls_classify_client_ciphers, or 0 if we haven't
+ * called that function yet. */
+ int8_t client_cipher_list_type;
+ /** 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
+ * tor_tls_get_n_raw_bytes() for usage.
+ */
+ unsigned long last_write_count;
+ unsigned long last_read_count;
+ /** If set, a callback to invoke whenever the client tries to renegotiate
+ * the handshake. */
+ void (*negotiated_callback)(tor_tls_t *tls, void *arg);
+ /** Argument to pass to negotiated_callback. */
+ void *callback_arg;
+};
+
+STATIC int tor_errno_to_tls_error(int e);
+STATIC int tor_tls_get_error(tor_tls_t *tls, int r, int extra,
+ const char *doing, int severity, int domain);
+STATIC tor_tls_t *tor_tls_get_by_ssl(const SSL *ssl);
+STATIC void tor_tls_allocate_tor_tls_object_ex_data_index(void);
+STATIC int always_accept_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx);
+STATIC int tor_tls_classify_client_ciphers(const SSL *ssl,
+ STACK_OF(SSL_CIPHER) *peer_ciphers);
+STATIC int tor_tls_client_is_using_v2_ciphers(const SSL *ssl);
+MOCK_DECL(STATIC void, try_to_extract_certs_from_tls,
+ (int severity, tor_tls_t *tls, X509 **cert_out, X509 **id_cert_out));
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+STATIC size_t SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out,
+ size_t len);
+#endif
+STATIC void tor_tls_debug_state_callback(const SSL *ssl, int type, int val);
+STATIC void tor_tls_server_info_callback(const SSL *ssl, int type, int val);
+STATIC int tor_tls_session_secret_cb(SSL *ssl, void *secret,
+ int *secret_len,
+ STACK_OF(SSL_CIPHER) *peer_ciphers,
+ SSL_CIPHER **cipher, void *arg);
+STATIC int find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m,
+ uint16_t cipher);
+MOCK_DECL(STATIC X509*, tor_tls_create_certificate,(crypto_pk_t *rsa,
+ crypto_pk_t *rsa_sign,
+ const char *cname,
+ const char *cname_sign,
+ unsigned int cert_lifetime));
+STATIC tor_tls_context_t *tor_tls_context_new(crypto_pk_t *identity,
+ unsigned int key_lifetime, unsigned flags, int is_client);
+MOCK_DECL(STATIC tor_x509_cert_t *, tor_x509_cert_new,(X509 *x509_cert));
+STATIC int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+ crypto_pk_t *identity,
+ unsigned int key_lifetime,
+ unsigned int flags,
+ int is_client);
+STATIC void tls_log_errors(tor_tls_t *tls, int severity, int domain,
+ const char *doing);
+#endif
+
const char *tor_tls_err_to_string(int err);
void tor_tls_get_state_description(tor_tls_t *tls, char *buf, size_t sz);
@@ -81,7 +195,6 @@ MOCK_DECL(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);
@@ -99,7 +212,6 @@ int tor_tls_get_buffer_sizes(tor_tls_t *tls,
MOCK_DECL(double, tls_get_write_overhead_ratio, (void));
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);
MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
diff --git a/src/common/util.c b/src/common/util.c
index b33c80fd45..ce3646cd64 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -488,42 +488,58 @@ round_to_power_of_2(uint64_t u64)
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as an unsigned, return
+ * UINT_MAX */
unsigned
round_to_next_multiple_of(unsigned number, unsigned divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT_MAX - divisor + 1 < number)
+ return UINT_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint32_t, return
+ * UINT32_MAX */
uint32_t
round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT32_MAX - divisor + 1 < number)
+ return UINT32_MAX;
+
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x such that x is at least <b>number</b>, and x modulo
- * <b>divisor</b> == 0. */
+ * <b>divisor</b> == 0. If no such x can be expressed as a uint64_t, return
+ * UINT64_MAX */
uint64_t
round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
{
+ tor_assert(divisor > 0);
+ if (UINT64_MAX - divisor + 1 < number)
+ return UINT64_MAX;
number += divisor - 1;
number -= number % divisor;
return number;
}
/** Return the lowest x in [INT64_MIN, INT64_MAX] such that x is at least
- * <b>number</b>, and x modulo <b>divisor</b> == 0. */
+ * <b>number</b>, and x modulo <b>divisor</b> == 0. If no such x can be
+ * expressed as an int64_t, return INT64_MAX */
int64_t
round_int64_to_next_multiple_of(int64_t number, int64_t divisor)
{
tor_assert(divisor > 0);
- if (number >= 0 && INT64_MAX - divisor + 1 >= number)
+ if (INT64_MAX - divisor + 1 < number)
+ return INT64_MAX;
+ if (number >= 0)
number += divisor - 1;
number -= number % divisor;
return number;
@@ -537,33 +553,44 @@ int64_t
sample_laplace_distribution(double mu, double b, double p)
{
double result;
-
tor_assert(p >= 0.0 && p < 1.0);
+
/* This is the "inverse cumulative distribution function" from:
* http://en.wikipedia.org/wiki/Laplace_distribution */
- result = mu - b * (p > 0.5 ? 1.0 : -1.0)
- * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
-
- if (result >= INT64_MAX)
- return INT64_MAX;
- else if (result <= INT64_MIN)
+ if (p <= 0.0) {
+ /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler
+ * options can cause the program to trap. */
return INT64_MIN;
- else
- return (int64_t) result;
+ }
+
+ result = mu - b * (p > 0.5 ? 1.0 : -1.0)
+ * tor_mathlog(1.0 - 2.0 * fabs(p - 0.5));
+
+ return clamp_double_to_int64(result);
}
-/** Add random noise between INT64_MIN and INT64_MAX coming from a
- * Laplace distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b>
- * to <b>signal</b> based on the provided <b>random</b> value in
- * [0.0, 1.0[. */
+/** Add random noise between INT64_MIN and INT64_MAX coming from a Laplace
+ * distribution with mu = 0 and b = <b>delta_f</b>/<b>epsilon</b> to
+ * <b>signal</b> based on the provided <b>random</b> value in [0.0, 1.0[.
+ * The epsilon value must be between ]0.0, 1.0]. delta_f must be greater
+ * than 0. */
int64_t
add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon)
{
- int64_t noise = sample_laplace_distribution(
- 0.0, /* just add noise, no further signal */
- delta_f / epsilon, random);
+ int64_t noise;
+
+ /* epsilon MUST be between ]0.0, 1.0] */
+ tor_assert(epsilon > 0.0 && epsilon <= 1.0);
+ /* delta_f MUST be greater than 0. */
+ tor_assert(delta_f > 0.0);
+ /* Just add noise, no further signal */
+ noise = sample_laplace_distribution(0.0,
+ delta_f / epsilon,
+ random);
+
+ /* Clip (signal + noise) to [INT64_MIN, INT64_MAX] */
if (noise > 0 && INT64_MAX - noise < signal)
return INT64_MAX;
else if (noise < 0 && INT64_MIN - noise > signal)
@@ -5385,3 +5412,38 @@ tor_weak_random_range(tor_weak_rng_t *rng, int32_t top)
return result;
}
+/** Cast a given double value to a int64_t. Return 0 if number is NaN.
+ * Returns either INT64_MIN or INT64_MAX if number is outside of the int64_t
+ * range. */
+int64_t
+clamp_double_to_int64(double number)
+{
+ int exp;
+
+ /* NaN is a special case that can't be used with the logic below. */
+ if (isnan(number)) {
+ return 0;
+ }
+
+ /* Time to validate if result can overflows a int64_t value. Fun with
+ * float! Find that exponent exp such that
+ * number == x * 2^exp
+ * for some x with abs(x) in [0.5, 1.0). Note that this implies that the
+ * magnitude of number is strictly less than 2^exp.
+ *
+ * If number is infinite, the call to frexp is legal but the contents of
+ * exp are unspecified. */
+ frexp(number, &exp);
+
+ /* If the magnitude of number is strictly less than 2^63, the truncated
+ * version of number is guaranteed to be representable. The only
+ * representable integer for which this is not the case is INT64_MIN, but
+ * it is covered by the logic below. */
+ if (isfinite(number) && exp <= 63) {
+ return number;
+ }
+
+ /* Handle infinities and finite numbers with magnitude >= 2^63. */
+ return signbit(number) ? INT64_MIN : INT64_MAX;
+}
+
diff --git a/src/common/util.h b/src/common/util.h
index 8bb4505e86..165bc0dcb3 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -185,6 +185,7 @@ int64_t sample_laplace_distribution(double mu, double b, double p);
int64_t add_laplace_noise(int64_t signal, double random, double delta_f,
double epsilon);
int n_bits_set_u8(uint8_t v);
+int64_t clamp_double_to_int64(double number);
/* 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