aboutsummaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/address.c19
-rw-r--r--src/common/address.h5
-rw-r--r--src/common/aes.c19
-rw-r--r--src/common/compat.h7
-rw-r--r--src/common/compat_libevent.c28
-rw-r--r--src/common/container.c1
-rw-r--r--src/common/crypto.c1
-rw-r--r--src/common/memarea.c5
-rw-r--r--src/common/tortls.c251
-rw-r--r--src/common/tortls.h10
-rw-r--r--src/common/util.c200
-rw-r--r--src/common/util.h26
12 files changed, 364 insertions, 208 deletions
diff --git a/src/common/address.c b/src/common/address.c
index c35f04c18..a3f12ac07 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -53,9 +53,7 @@
* socklen object in *<b>sa_out</b> of object size <b>len</b>. If not enough
* room is free, or on error, return -1. Else return the length of the
* sockaddr. */
-/* XXXX021 This returns socklen_t. socklen_t is sometimes unsigned. This
- * function claims to return -1 sometimes. Problematic! */
-socklen_t
+int
tor_addr_to_sockaddr(const tor_addr_t *a,
uint16_t port,
struct sockaddr *sa_out,
@@ -311,7 +309,7 @@ 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);
@@ -927,6 +925,19 @@ fmt_addr(const tor_addr_t *addr)
return buf;
}
+/** 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.
diff --git a/src/common/address.h b/src/common/address.h
index 6116bb4b1..e004ffb13 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -39,7 +39,7 @@ static INLINE sa_family_t tor_addr_family(const tor_addr_t *a);
static INLINE const struct in_addr *tor_addr_to_in(const tor_addr_t *a);
static INLINE int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u);
-socklen_t tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port,
+int tor_addr_to_sockaddr(const tor_addr_t *a, uint16_t port,
struct sockaddr *sa_out, socklen_t len);
int tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa,
uint16_t *port_out);
@@ -108,6 +108,7 @@ 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);
+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"
@@ -144,7 +145,7 @@ int tor_addr_port_parse(const char *s, tor_addr_t *addr_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);
void tor_addr_copy(tor_addr_t *dest, const tor_addr_t *src);
diff --git a/src/common/aes.c b/src/common/aes.c
index a17328317..4998c386a 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -288,11 +288,20 @@ 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. */
+ /* This function alone is up to 5% of our runtime in some profiles; anything
+ * we could do to make it faster would be great.
+ *
+ * Experimenting suggests that unrolling the inner loop into a switch
+ * statement doesn't help. What does seem to help is making the input and
+ * output buffers word aligned, and never crypting anything besides an
+ * integer number of words at a time -- it shaves maybe 4-5% of the per-byte
+ * encryption time measured by bench_aes. We can't do that with the current
+ * Tor protocol, though: Tor really likes to crypt things in 509-byte
+ * chunks.
+ *
+ * If we were really ambitous, we'd force len to be a multiple of the block
+ * size, and shave maybe another 4-5% off.
+ */
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
diff --git a/src/common/compat.h b/src/common/compat.h
index 5b6cfc40b..2471e6b83 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -332,6 +332,13 @@ struct tm *tor_localtime_r(const time_t *timep, struct tm *result);
struct tm *tor_gmtime_r(const time_t *timep, struct tm *result);
#endif
+/** Return true iff the tvp is related to uvp according to the relational
+ * operator cmp. Recognized values for cmp are ==, <=, <, >=, and >. */
+#define tor_timercmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+
/* ===== File compatibility */
int replace_file(const char *from, const char *to);
int touch_file(const char *fname);
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index d94f615f7..d3b9eb3af 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -187,6 +187,8 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
#endif
the_event_base = event_base_new_with_config(cfg);
+
+ event_config_free(cfg);
}
#else
the_event_base = event_init();
@@ -340,17 +342,21 @@ tor_check_libevent_version(const char *m, int server,
version = tor_get_libevent_version(&v);
- /* XXX Would it be worthwhile disabling the methods that we know
- * are buggy, rather than just warning about them and then proceeding
- * to use them? If so, we should probably not wrap this whole thing
- * in HAVE_EVENT_GET_VERSION and HAVE_EVENT_GET_METHOD. -RD */
- /* XXXX The problem is that it's not trivial to get libevent to change it's
- * method once it's initialized, and it's not trivial to tell what method it
- * will use without initializing it. I guess we could preemptively disable
- * buggy libevent modes based on the version _before_ initializing it,
- * though, but then there's no good way (afaict) to warn "I would have used
- * kqueue, but instead I'm using select." -NM */
- /* XXXX022 revist the above; it is fixable now. */
+ /* It would be better to disable known-buggy methods than to simply
+ warn about them. However, it's not trivial to get libevent to change its
+ method once it's initialized, and it's not trivial to tell what method it
+ will use without initializing it.
+
+ If we saw that the version was definitely bad, we could disable all the
+ methods that were bad for that version. But the issue with that is that
+ if you've found a libevent before 1.1, you are not at all guaranteed to
+ have _any_ good method to use.
+
+ As of Libevent 2, we can do better, and have more control over what
+ methods get used. But the problem here is that there are no versions of
+ Libevent 2 that have buggy event cores, so there's no point in writing
+ disable code yet.
+ */
if (!strcmp(m, "kqueue")) {
if (version < V_OLD(1,1,'b'))
buggy = 1;
diff --git a/src/common/container.c b/src/common/container.c
index 72f347034..0a95f33aa 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -268,7 +268,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)
diff --git a/src/common/crypto.c b/src/common/crypto.c
index bfb81d3cc..b49547fa4 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -405,6 +405,7 @@ crypto_free_pk_env(crypto_pk_env_t *env)
if (--env->refs > 0)
return;
+ tor_assert(env->refs == 0);
if (env->key)
RSA_free(env->key);
diff --git a/src/common/memarea.c b/src/common/memarea.c
index 77579e63b..b5ac7ad97 100644
--- a/src/common/memarea.c
+++ b/src/common/memarea.c
@@ -53,7 +53,7 @@ realign_pointer(void *ptr)
{
uintptr_t x = (uintptr_t)ptr;
x = (x+MEMAREA_ALIGN_MASK) & ~MEMAREA_ALIGN_MASK;
- tor_assert(((void*)x) >= ptr); // XXXX021 remove this once bug 930 is solved
+ tor_assert(((void*)x) >= ptr);
return (void*)x;
}
@@ -230,9 +230,10 @@ memarea_alloc(memarea_t *area, size_t sz)
}
result = chunk->next_mem;
chunk->next_mem = chunk->next_mem + sz;
- // XXXX021 remove these once bug 930 is solved.
+
tor_assert(chunk->next_mem >= chunk->u.mem);
tor_assert(chunk->next_mem <= chunk->u.mem+chunk->mem_size);
+
chunk->next_mem = realign_pointer(chunk->next_mem);
return result;
}
diff --git a/src/common/tortls.c b/src/common/tortls.c
index ce5411a55..ba450e4b1 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -93,7 +93,9 @@ static int use_unsafe_renegotiation_op = 0;
* SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
static int use_unsafe_renegotiation_flag = 0;
-/** Structure holding the TLS state for a single connection. */
+/** Holds a SSL_CTX object and related state used to configure TLS
+ * connections.
+ */
typedef struct tor_tls_context_t {
int refcnt;
SSL_CTX *ctx;
@@ -123,8 +125,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
@@ -196,9 +200,17 @@ static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa,
const char *cname_sign,
unsigned int lifetime);
-/** Global tls context. We keep it here because nobody else needs to
- * touch it. */
-static tor_tls_context_t *global_tls_context = NULL;
+static int tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+ crypto_pk_env_t *identity,
+ unsigned int key_lifetime);
+static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity,
+ unsigned int key_lifetime);
+
+/** 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;
@@ -222,36 +234,46 @@ ssl_state_to_string(int ssl_state)
return buf;
}
+void
+tor_tls_log_one_error(tor_tls_t *tls, unsigned long err,
+ int severity, int domain, const char *doing)
+{
+ const char *state = NULL, *addr;
+ const char *msg, *lib, *func;
+ int st;
+
+ st = (tls && tls->ssl) ? tls->ssl->state : -1;
+ state = (st>=0)?ssl_state_to_string(st):"---";
+
+ addr = tls ? tls->address : NULL;
+
+ 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);
+ }
+}
+
/** Log all pending tls errors at level <b>severity</b>. Use
* <b>doing</b> to describe our current activities.
*/
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);
}
}
@@ -379,7 +401,7 @@ tor_tls_init(void)
version = SSLeay();
- /* OpenSSL 0.9.8l introduced SSL3_FLAGS_ALLOW_UNSAGE_LEGACY_RENEGOTIATION
+ /* OpenSSL 0.9.8l introduced SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
* here, but without thinking too hard about it: it turns out that the
* flag in question needed to be set at the last minute, and that it
* conflicted with an existing flag number that had already been added
@@ -434,9 +456,15 @@ tor_tls_init(void)
void
tor_tls_free_all(void)
{
- if (global_tls_context) {
- tor_tls_context_decref(global_tls_context);
- global_tls_context = NULL;
+ if (server_tls_context) {
+ tor_tls_context_t *ctx = server_tls_context;
+ server_tls_context = NULL;
+ tor_tls_context_decref(ctx);
+ }
+ if (client_tls_context) {
+ tor_tls_context_t *ctx = client_tls_context;
+ 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.");
@@ -562,9 +590,9 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa,
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
-/* Note: for setting up your own private testing network with link crypto
- * disabled, set the cipher lists to your cipher list to
- * SSL3_TXT_RSA_NULL_SHA. If you do this, you won't be able to communicate
+/* Note: to set up your own private testing network with link crypto
+ * disabled, set your Tors' cipher list to
+ * (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
* with any of the "real" Tors, though. */
#ifdef V2_HANDSHAKE_CLIENT
@@ -623,15 +651,97 @@ tor_tls_context_incref(tor_tls_context_t *ctx)
++ctx->refcnt;
}
-/** Create a new TLS context for use with Tor TLS handshakes.
- * <b>identity</b> should be set to the identity key used to sign the
- * certificate, and <b>nickname</b> set to the nickname to use.
+/** Create new global client and server TLS contexts.
+ *
+ * If <b>server_identity</b> is NULL, this will not generate a server
+ * TLS context. If <b>is_public_server</b> is non-zero, this will use
+ * the same TLS context for incoming and outgoing connections, and
+ * 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,
+ unsigned int key_lifetime)
+{
+ int rv1 = 0;
+ int rv2 = 0;
+
+ if (is_public_server) {
+ tor_tls_context_t *new_ctx;
+ tor_tls_context_t *old_ctx;
+
+ tor_assert(server_identity != NULL);
+
+ rv1 = tor_tls_context_init_one(&server_tls_context,
+ server_identity,
+ key_lifetime);
+
+ if (rv1 >= 0) {
+ new_ctx = server_tls_context;
+ tor_tls_context_incref(new_ctx);
+ old_ctx = client_tls_context;
+ client_tls_context = new_ctx;
+
+ if (old_ctx != NULL) {
+ tor_tls_context_decref(old_ctx);
+ }
+ }
+ } else {
+ if (server_identity != NULL) {
+ rv1 = tor_tls_context_init_one(&server_tls_context,
+ server_identity,
+ key_lifetime);
+ } else {
+ tor_tls_context_t *old_ctx = server_tls_context;
+ server_tls_context = NULL;
+
+ if (old_ctx != NULL) {
+ tor_tls_context_decref(old_ctx);
+ }
+ }
+
+ rv2 = tor_tls_context_init_one(&client_tls_context,
+ client_identity,
+ key_lifetime);
+ }
+
+ return MIN(rv1, rv2);
+}
+
+/** Create a new global TLS context.
*
* You can call this function multiple times. Each time you call it,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
-int
+static int
+tor_tls_context_init_one(tor_tls_context_t **ppcontext,
+ crypto_pk_env_t *identity,
+ unsigned int key_lifetime)
+{
+ tor_tls_context_t *new_ctx = tor_tls_context_new(identity,
+ key_lifetime);
+ tor_tls_context_t *old_ctx = *ppcontext;
+
+ if (new_ctx != NULL) {
+ *ppcontext = new_ctx;
+
+ /* Free the old context if one existed. */
+ if (old_ctx != NULL) {
+ /* This is safe even if there are open connections: we reference-
+ * count tor_tls_context_t objects. */
+ tor_tls_context_decref(old_ctx);
+ }
+ }
+
+ return ((new_ctx != NULL) ? 0 : -1);
+}
+
+/** Create a new TLS context for use with Tor TLS handshakes.
+ * <b>identity</b> should be set to the identity key used to sign the
+ * certificate.
+ */
+static tor_tls_context_t *
tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
{
crypto_pk_env_t *rsa = NULL;
@@ -726,18 +836,12 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
- /* Free the old context if one exists. */
- if (global_tls_context) {
- /* This is safe even if there are open connections: OpenSSL does
- * reference counting with SSL and SSL_CTX objects. */
- tor_tls_context_decref(global_tls_context);
- }
- global_tls_context = result;
+
if (rsa)
crypto_free_pk_env(rsa);
tor_free(nickname);
tor_free(nn2);
- return 0;
+ return result;
error:
tls_log_errors(NULL, LOG_WARN, LD_NET, "creating TLS context");
@@ -753,7 +857,7 @@ tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
X509_free(cert);
if (idcert)
X509_free(idcert);
- return -1;
+ return NULL;
}
#ifdef V2_HANDSHAKE_SERVER
@@ -829,6 +933,8 @@ 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!");
}
@@ -847,6 +953,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!");
}
@@ -932,10 +1042,12 @@ tor_tls_new(int sock, int isServer)
{
BIO *bio = NULL;
tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t));
+ tor_tls_context_t *context = isServer ? server_tls_context :
+ client_tls_context;
- tor_assert(global_tls_context); /* make sure somebody made it first */
- if (!(result->ssl = SSL_new(global_tls_context->ctx))) {
- tls_log_errors(NULL, LOG_WARN, LD_NET, "generating TLS context");
+ tor_assert(context); /* make sure somebody made it first */
+ if (!(result->ssl = SSL_new(context->ctx))) {
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating SSL object");
tor_free(result);
return NULL;
}
@@ -974,8 +1086,8 @@ tor_tls_new(int sock, int isServer)
}
HT_INSERT(tlsmap, &tlsmap_root, result);
SSL_set_bio(result->ssl, bio, bio);
- tor_tls_context_incref(global_tls_context);
- result->context = global_tls_context;
+ tor_tls_context_incref(context);
+ result->context = context;
result->state = TOR_TLS_ST_HANDSHAKE;
result->isServer = isServer;
result->wantwrite_n = 0;
@@ -992,7 +1104,7 @@ tor_tls_new(int sock, int isServer)
#endif
/* Not expected to get called. */
- tls_log_errors(NULL, LOG_WARN, LD_NET, "generating TLS context");
+ tls_log_errors(NULL, LOG_WARN, LD_NET, "creating tor_tls_t object");
return result;
}
@@ -1055,6 +1167,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
@@ -1646,6 +1771,22 @@ tor_tls_used_v1_handshake(tor_tls_t *tls)
return 1;
}
+/** 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;
+}
+
/** 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.
@@ -1720,6 +1861,10 @@ tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
state,
BEV_OPT_DEFER_CALLBACKS);
#endif
+ /* 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 f8603b529..50d14da52 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -50,7 +50,10 @@ typedef struct tor_tls_t tor_tls_t;
const char *tor_tls_err_to_string(int err);
void tor_tls_free_all(void);
-int tor_tls_context_new(crypto_pk_env_t *rsa, unsigned int key_lifetime);
+int tor_tls_context_init(int is_public_server,
+ crypto_pk_env_t *client_identity,
+ crypto_pk_env_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);
void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
@@ -68,6 +71,7 @@ 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);
@@ -80,12 +84,16 @@ 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_get_num_server_handshakes(tor_tls_t *tls);
+int tor_tls_server_got_renegotiate(tor_tls_t *tls);
/* 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);
diff --git a/src/common/util.c b/src/common/util.c
index b5a3ade2b..16e737023 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -934,7 +934,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) {
@@ -1503,83 +1503,6 @@ update_approx_time(time_t now)
#endif
/* =====
- * Fuzzy time
- * XXXX022 Use this consistently or rip most of it out.
- * ===== */
-
-/* In a perfect world, everybody would run NTP, and NTP would be perfect, so
- * if we wanted to know "Is the current time before time X?" we could just say
- * "time(NULL) < X".
- *
- * But unfortunately, many users are running Tor in an imperfect world, on
- * even more imperfect computers. Hence, we need to track time oddly. We
- * model the user's computer as being "skewed" from accurate time by
- * -<b>ftime_skew</b> seconds, such that our best guess of the current time is
- * time(NULL)+ftime_skew. We also assume that our measurements of time may
- * have up to <b>ftime_slop</b> seconds of inaccuracy; IOW, our window of
- * estimate for the current time is now + ftime_skew +/- ftime_slop.
- */
-/** Our current estimate of our skew, such that we think the current time is
- * closest to time(NULL)+ftime_skew. */
-static int ftime_skew = 0;
-/** Tolerance during time comparisons, in seconds. */
-static int ftime_slop = 60;
-/** Set the largest amount of sloppiness we'll allow in fuzzy time
- * comparisons. */
-void
-ftime_set_maximum_sloppiness(int seconds)
-{
- tor_assert(seconds >= 0);
- ftime_slop = seconds;
-}
-/** Set the amount by which we believe our system clock to differ from
- * real time. */
-void
-ftime_set_estimated_skew(int seconds)
-{
- ftime_skew = seconds;
-}
-#if 0
-void
-ftime_get_window(time_t now, ftime_t *ft_out)
-{
- ft_out->earliest = now + ftime_skew - ftime_slop;
- ft_out->latest = now + ftime_skew + ftime_slop;
-}
-#endif
-/** Return true iff we think that <b>now</b> might be after <b>when</b>. */
-int
-ftime_maybe_after(time_t now, time_t when)
-{
- /* It may be after when iff the latest possible current time is after when */
- return (now + ftime_skew + ftime_slop) >= when;
-}
-/** Return true iff we think that <b>now</b> might be before <b>when</b>. */
-int
-ftime_maybe_before(time_t now, time_t when)
-{
- /* It may be before when iff the earliest possible current time is before */
- return (now + ftime_skew - ftime_slop) < when;
-}
-/** Return true if we think that <b>now</b> is definitely after <b>when</b>. */
-int
-ftime_definitely_after(time_t now, time_t when)
-{
- /* It is definitely after when if the earliest time it could be is still
- * after when. */
- return (now + ftime_skew - ftime_slop) >= when;
-}
-/** Return true if we think that <b>now</b> is definitely before <b>when</b>.
- */
-int
-ftime_definitely_before(time_t now, time_t when)
-{
- /* It is definitely before when if the latest time it could be is still
- * before when. */
- return (now + ftime_skew + ftime_slop) < when;
-}
-
-/* =====
* Rate limiting
* ===== */
@@ -2498,18 +2421,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;
@@ -2571,11 +2497,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;
@@ -2612,9 +2539,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 and %x 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
@@ -2879,17 +2806,34 @@ load_windows_system_library(const TCHAR *library_name)
}
#endif
-/** Format child_state and saved_errno as a hex string placed in hex_errno.
- * Called between fork and _exit, so must be signal-handler safe */
+/** 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)
{
- /* Convert errno to be unsigned for hex conversion */
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';
- /* If errno is negative, negate it */
+ /* Convert errno to be unsigned for hex conversion */
if (saved_errno < 0) {
unsigned_errno = (unsigned int) -saved_errno;
} else {
@@ -2899,17 +2843,26 @@ format_helper_exit_status(unsigned char child_state, 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);
- /* Add on the minus side if errno was negative */
- if (saved_errno < 0)
+ /* Prepend the minus sign if errno was negative */
+ if (saved_errno < 0 && cur >= hex_errno)
*cur-- = '-';
/* Leave a gap */
- *cur-- = '/';
+ 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 {
@@ -2934,13 +2887,20 @@ format_helper_exit_status(unsigned char child_state, int saved_errno,
#define SPAWN_ERROR_MESSAGE "ERR: Failed to spawn background process - code "
-/** Start a program in the background. If <b>filename</b> contains a '/', then
- * it will be treated as an absolute or relative path. Otherwise the system
- * path will be searched for <b>filename</b>. Returns pid on success, otherwise
- * returns -1.
- * Some parts of this code are based on the POSIX subprocess module from Python
+/** Start a program in the background. If <b>filename</b> contains a '/',
+ * then it will be treated as an absolute or relative path. Otherwise the
+ * system path will be searched for <b>filename</b>. 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). The last element of argv must be NULL. If the child
+ * program is launched, the PID will be returned and <b>stdout_read</b> and
+ * <b>stdout_err</b> will be set to file descriptors from which the stdout
+ * and stderr, respectively, output of the child program can be read, and the
+ * stdin of the child process shall be set to /dev/null. Otherwise returns
+ * -1. Some parts of this code are based on the POSIX subprocess module from
+ * Python.
*/
-static int
+int
tor_spawn_background(const char *const filename, int *stdout_read,
int *stderr_read, const char **argv)
{
@@ -2970,16 +2930,12 @@ tor_spawn_background(const char *const filename, int *stdout_read,
and we are not allowed to use unsafe functions between fork and exec */
error_message_length = strlen(error_message);
- /* Fill hex_errno with spaces, and a trailing newline */
- memset(hex_errno, ' ', sizeof(hex_errno) - 1);
- hex_errno[sizeof(hex_errno) - 1] = '\n';
-
child_state = CHILD_STATE_PIPE;
/* Set up pipe for redirecting stdout and stderr of child */
retval = pipe(stdout_pipe);
if (-1 == retval) {
- log_err(LD_GENERAL,
+ log_warn(LD_GENERAL,
"Failed to set up pipe for stdout communication with child process: %s",
strerror(errno));
return -1;
@@ -2987,7 +2943,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
retval = pipe(stderr_pipe);
if (-1 == retval) {
- log_err(LD_GENERAL,
+ log_warn(LD_GENERAL,
"Failed to set up pipe for stderr communication with child process: %s",
strerror(errno));
return -1;
@@ -3039,7 +2995,8 @@ tor_spawn_background(const char *const filename, int *stdout_read,
child_state = CHILD_STATE_CLOSEFD;
/* Close all other fds, including the read end of the pipe */
- /* TODO: use closefrom if available */
+ /* XXX: use closefrom if available, or better still set FD_CLOEXEC
+ on all of Tor's open files */
for (fd = STDERR_FILENO + 1; fd < max_fd; fd++)
close(fd);
@@ -3055,7 +3012,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
child_state = CHILD_STATE_FAILEXEC;
error:
- /* TODO: are we leaking fds from the pipe? */
+ /* XXX: are we leaking fds from the pipe? */
format_helper_exit_status(child_state, errno, hex_errno);
@@ -3071,7 +3028,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
/* In parent */
if (-1 == pid) {
- log_err(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
+ log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[0]);
@@ -3084,7 +3041,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
retval = close(stdout_pipe[1]);
if (-1 == retval) {
- log_err(LD_GENERAL,
+ log_warn(LD_GENERAL,
"Failed to close write end of stdout pipe in parent process: %s",
strerror(errno));
/* Do not return -1, because the child is running, so the parent
@@ -3095,7 +3052,7 @@ tor_spawn_background(const char *const filename, int *stdout_read,
retval = close(stderr_pipe[1]);
if (-1 == retval) {
- log_err(LD_GENERAL,
+ log_warn(LD_GENERAL,
"Failed to close write end of stderr pipe in parent process: %s",
strerror(errno));
/* Do not return -1, because the child is running, so the parent
@@ -3148,26 +3105,27 @@ log_from_pipe(FILE *stream, int severity, const char *executable,
} else {
/* No newline; check whether we overflowed the buffer */
if (!feof(stream))
- log_err(LD_GENERAL,
+ log_warn(LD_GENERAL,
"Line from port forwarding helper was truncated: %s", buf);
/* TODO: What to do with this error? */
}
/* Check if buf starts with SPAWN_ERROR_MESSAGE */
- if (strstr(buf, SPAWN_ERROR_MESSAGE) == buf) {
+ if (strcmpstart(buf, SPAWN_ERROR_MESSAGE) == 0) {
/* Parse error message */
int retval, child_state, saved_errno;
- retval = sscanf(buf, SPAWN_ERROR_MESSAGE "%d/%d",
- &child_state, &saved_errno);
+ retval = tor_sscanf(buf, SPAWN_ERROR_MESSAGE "%x/%x",
+ &child_state, &saved_errno);
if (retval == 2) {
- log_err(LD_GENERAL,
+ 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 error */
- log_err(LD_GENERAL,
+ /* 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);
}
@@ -3233,7 +3191,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
child_pid = tor_spawn_background(filename, &fd_out, &fd_err, argv);
if (child_pid < 0) {
- log_err(LD_GENERAL, "Failed to start port forwarding helper %s",
+ log_warn(LD_GENERAL, "Failed to start port forwarding helper %s",
filename);
child_pid = -1;
return;
@@ -3254,7 +3212,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
/* Read from stdout/stderr and log result */
retval = 0;
stdout_status = log_from_pipe(stdout_read, LOG_INFO, filename, &retval);
- stderr_status = log_from_pipe(stderr_read, LOG_ERR, filename, &retval);
+ stderr_status = log_from_pipe(stderr_read, LOG_WARN, filename, &retval);
if (retval) {
/* There was a problem in the child process */
time_to_run_helper = now + TIME_TO_EXEC_FWHELPER_FAIL;
@@ -3276,7 +3234,7 @@ tor_check_port_forwarding(const char *filename, int dir_port, int or_port,
if (1 == retval) {
log_info(LD_GENERAL, "Port forwarding helper terminated");
} else {
- log_err(LD_GENERAL, "Failed to read from port forwarding helper");
+ log_warn(LD_GENERAL, "Failed to read from port forwarding helper");
}
/* TODO: The child might not actually be finished (maybe it failed or
diff --git a/src/common/util.h b/src/common/util.h
index 86555eeb1..633c613d4 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -248,14 +248,22 @@ void update_approx_time(time_t now);
#endif
/* Fuzzy time. */
-void ftime_set_maximum_sloppiness(int seconds);
-void ftime_set_estimated_skew(int seconds);
-/* typedef struct ftime_t { time_t earliest; time_t latest; } ftime_t; */
-/* void ftime_get_window(time_t now, ftime_t *ft_out); */
-int ftime_maybe_after(time_t now, time_t when);
-int ftime_maybe_before(time_t now, time_t when);
-int ftime_definitely_after(time_t now, time_t when);
-int ftime_definitely_before(time_t now, time_t when);
+
+/** Return true iff <a>a</b> is definitely after <b>b</b>, even if there
+ * could be up to <b>allow_seconds</b> of skew in one of them. */
+static INLINE int
+time_definitely_after(time_t a, time_t b, int allow_skew)
+{
+ return a-allow_skew > b;
+}
+
+/** Return true iff <a>a</b> is definitely before <b>b</b>, even if there
+ * could be up to <b>allow_seconds</b> of skew in one of them. */
+static INLINE int
+time_definitely_before(time_t a, time_t b, int allow_skew)
+{
+ return a+allow_skew < b;
+}
/* Rate-limiter */
@@ -350,6 +358,8 @@ HANDLE load_windows_system_library(const TCHAR *library_name);
#ifdef UTIL_PRIVATE
/* Prototypes for private functions only used by util.c (and unit tests) */
+int tor_spawn_background(const char *const filename, int *stdout_read,
+ int *stderr_read, const char **argv);
void format_helper_exit_status(unsigned char child_state,
int saved_errno, char *hex_errno);