diff options
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/address.c | 19 | ||||
-rw-r--r-- | src/common/address.h | 5 | ||||
-rw-r--r-- | src/common/aes.c | 19 | ||||
-rw-r--r-- | src/common/compat.h | 7 | ||||
-rw-r--r-- | src/common/compat_libevent.c | 28 | ||||
-rw-r--r-- | src/common/container.c | 1 | ||||
-rw-r--r-- | src/common/crypto.c | 1 | ||||
-rw-r--r-- | src/common/memarea.c | 5 | ||||
-rw-r--r-- | src/common/tortls.c | 251 | ||||
-rw-r--r-- | src/common/tortls.h | 10 | ||||
-rw-r--r-- | src/common/util.c | 200 | ||||
-rw-r--r-- | src/common/util.h | 26 |
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); |