diff options
Diffstat (limited to 'src/or')
129 files changed, 15769 insertions, 4616 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c index 9c29fb2acb..047a863ef5 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -1,9 +1,18 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file addressmap.c + * + * \brief The addressmap module manages the processes by which we rewrite + * addresses in client requess. It handles the MapAddress controller and + * torrc commands, and the TrackHostExits feature, and the client-side DNS + * cache (deprecated). + */ + #define ADDRESSMAP_PRIVATE #include "or.h" diff --git a/src/or/addressmap.h b/src/or/addressmap.h index ff108df024..67648d0518 100644 --- a/src/or/addressmap.h +++ b/src/or/addressmap.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_ADDRESSMAP_H diff --git a/src/or/buffers.c b/src/or/buffers.c index 74aebccb77..a41af5f429 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1,14 +1,15 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file buffers.c * \brief Implements a generic interface buffer. Buffers are * fairly opaque string holders that can read to or flush from: - * memory, file descriptors, or TLS connections. + * memory, file descriptors, or TLS connections. Buffers are implemented + * as linked lists of memory chunks. **/ #define BUFFERS_PRIVATE #include "or.h" @@ -20,8 +21,8 @@ #include "control.h" #include "reasons.h" #include "ext_orport.h" -#include "../common/util.h" -#include "../common/torlog.h" +#include "util.h" +#include "torlog.h" #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -99,7 +100,7 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, /** Return the next character in <b>chunk</b> onto which data can be appended. * If the chunk is full, this might be off the end of chunk->mem. */ -static INLINE char * +static inline char * CHUNK_WRITE_PTR(chunk_t *chunk) { return chunk->data + chunk->datalen; @@ -107,7 +108,7 @@ CHUNK_WRITE_PTR(chunk_t *chunk) /** Return the number of bytes that can be written onto <b>chunk</b> without * running out of space. */ -static INLINE size_t +static inline size_t CHUNK_REMAINING_CAPACITY(const chunk_t *chunk) { return (chunk->mem + chunk->memlen) - (chunk->data + chunk->datalen); @@ -115,7 +116,7 @@ CHUNK_REMAINING_CAPACITY(const chunk_t *chunk) /** Move all bytes stored in <b>chunk</b> to the front of <b>chunk</b>->mem, * to free up space at the end. */ -static INLINE void +static inline void chunk_repack(chunk_t *chunk) { if (chunk->datalen && chunk->data != &chunk->mem[0]) { @@ -139,7 +140,7 @@ chunk_free_unchecked(chunk_t *chunk) total_bytes_allocated_in_chunks -= CHUNK_ALLOC_SIZE(chunk->memlen); tor_free(chunk); } -static INLINE chunk_t * +static inline chunk_t * chunk_new_with_alloc_size(size_t alloc) { chunk_t *ch; @@ -158,7 +159,7 @@ chunk_new_with_alloc_size(size_t alloc) /** Expand <b>chunk</b> until it can hold <b>sz</b> bytes, and return a * new pointer to <b>chunk</b>. Old pointers are no longer valid. */ -static INLINE chunk_t * +static inline chunk_t * chunk_grow(chunk_t *chunk, size_t sz) { off_t offset; @@ -189,7 +190,7 @@ chunk_grow(chunk_t *chunk, size_t sz) /** Return the allocation size we'd like to use to hold <b>target</b> * bytes. */ -static INLINE size_t +static inline size_t preferred_chunk_size(size_t target) { size_t sz = MIN_CHUNK_ALLOC; @@ -202,13 +203,10 @@ preferred_chunk_size(size_t target) /** Collapse data from the first N chunks from <b>buf</b> into buf->head, * growing it as necessary, until buf->head has the first <b>bytes</b> bytes * of data from the buffer, or until buf->head has all the data in <b>buf</b>. - * - * If <b>nulterminate</b> is true, ensure that there is a 0 byte in - * buf->head->mem right after all the data. */ + */ STATIC void -buf_pullup(buf_t *buf, size_t bytes, int nulterminate) +buf_pullup(buf_t *buf, size_t bytes) { - /* XXXX nothing uses nulterminate; remove it. */ chunk_t *dest, *src; size_t capacity; if (!buf->head) @@ -218,17 +216,9 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate) if (buf->datalen < bytes) bytes = buf->datalen; - if (nulterminate) { - capacity = bytes + 1; - if (buf->head->datalen >= bytes && CHUNK_REMAINING_CAPACITY(buf->head)) { - *CHUNK_WRITE_PTR(buf->head) = '\0'; - return; - } - } else { - capacity = bytes; - if (buf->head->datalen >= bytes) - return; - } + capacity = bytes; + if (buf->head->datalen >= bytes) + return; if (buf->head->memlen >= capacity) { /* We don't need to grow the first chunk, but we might need to repack it.*/ @@ -272,11 +262,6 @@ buf_pullup(buf_t *buf, size_t bytes, int nulterminate) } } - if (nulterminate) { - tor_assert(CHUNK_REMAINING_CAPACITY(buf->head)); - *CHUNK_WRITE_PTR(buf->head) = '\0'; - } - check(); } @@ -295,7 +280,7 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz) #endif /** Remove the first <b>n</b> bytes from buf. */ -static INLINE void +static inline void buf_remove_from_front(buf_t *buf, size_t n) { tor_assert(buf->datalen >= n); @@ -492,7 +477,7 @@ buf_get_total_allocation(void) * <b>chunk</b> (which must be on <b>buf</b>). If we get an EOF, set * *<b>reached_eof</b> to 1. Return -1 on error, 0 on eof or blocking, * and the number of bytes read otherwise. */ -static INLINE int +static inline int read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most, int *reached_eof, int *socket_error) { @@ -528,7 +513,7 @@ read_to_chunk(buf_t *buf, chunk_t *chunk, tor_socket_t fd, size_t at_most, /** As read_to_chunk(), but return (negative) error code on error, blocking, * or TLS, and the number of bytes read otherwise. */ -static INLINE int +static inline int read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls, size_t at_most) { @@ -639,7 +624,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) if (r < 0) return r; /* Error */ tor_assert(total_read+r < INT_MAX); - total_read += r; + total_read += r; if ((size_t)r < readlen) /* eof, block, or no more to read. */ break; } @@ -651,7 +636,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) * the bytes written from *<b>buf_flushlen</b>. Return the number of bytes * written on success, 0 on blocking, -1 on failure. */ -static INLINE int +static inline int flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz, size_t *buf_flushlen) { @@ -686,7 +671,7 @@ flush_chunk(tor_socket_t s, buf_t *buf, chunk_t *chunk, size_t sz, * bytes written from *<b>buf_flushlen</b>. Return the number of bytes * written on success, and a TOR_TLS error code on failure or blocking. */ -static INLINE int +static inline int flush_chunk_tls(tor_tls_t *tls, buf_t *buf, chunk_t *chunk, size_t sz, size_t *buf_flushlen) { @@ -837,7 +822,7 @@ write_to_buf(const char *string, size_t string_len, buf_t *buf) /** Helper: copy the first <b>string_len</b> bytes from <b>buf</b> * onto <b>string</b>. */ -static INLINE void +static inline void peek_from_buf(char *string, size_t string_len, const buf_t *buf) { chunk_t *chunk; @@ -882,7 +867,7 @@ fetch_from_buf(char *string, size_t string_len, buf_t *buf) /** True iff the cell command <b>command</b> is one that implies a * variable-length cell in Tor link protocol <b>linkproto</b>. */ -static INLINE int +static inline int cell_command_is_var_length(uint8_t command, int linkproto) { /* If linkproto is v2 (2), CELL_VERSIONS is the only variable-length cells @@ -1123,7 +1108,7 @@ buf_find_pos_of_char(char ch, buf_pos_t *out) /** Advance <b>pos</b> by a single character, if there are any more characters * in the buffer. Returns 0 on success, -1 on failure. */ -static INLINE int +static inline int buf_pos_inc(buf_pos_t *pos) { ++pos->pos; @@ -1227,7 +1212,7 @@ fetch_from_buf_http(buf_t *buf, /* Okay, we have a full header. Make sure it all appears in the first * chunk. */ if ((int)buf->head->datalen < crlf_offset + 4) - buf_pullup(buf, crlf_offset+4, 0); + buf_pullup(buf, crlf_offset+4); headerlen = crlf_offset + 4; headers = buf->head->data; @@ -1475,7 +1460,7 @@ fetch_from_buf_socks(buf_t *buf, socks_request_t *req, do { n_drain = 0; - buf_pullup(buf, want_length, 0); + buf_pullup(buf, want_length); tor_assert(buf->head && buf->head->datalen >= 2); want_length = 0; @@ -1866,7 +1851,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, log_warn(LD_PROTOCOL, "Your application (using socks5 to port %d) gave Tor " "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped(req->address)); + req->port, escaped_safe_str_client(req->address)); return -1; } if (log_sockstype) @@ -1894,7 +1879,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, *want_length_out = SOCKS4_NETWORK_LEN; return 0; /* not yet */ } - // buf_pullup(buf, 1280, 0); + // buf_pullup(buf, 1280); req->command = (unsigned char) *(data+1); if (req->command != SOCKS_COMMAND_CONNECT && req->command != SOCKS_COMMAND_RESOLVE) { @@ -1985,7 +1970,7 @@ parse_socks(const char *data, size_t datalen, socks_request_t *req, log_warn(LD_PROTOCOL, "Your application (using socks4 to port %d) gave Tor " "a malformed hostname: %s. Rejecting the connection.", - req->port, escaped(req->address)); + req->port, escaped_safe_str_client(req->address)); return -1; } if (authend != authstart) { @@ -2062,7 +2047,7 @@ fetch_from_buf_socks_client(buf_t *buf, int state, char **reason) if (buf->datalen < 2) return 0; - buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN, 0); + buf_pullup(buf, MAX_SOCKS_MESSAGE_LEN); tor_assert(buf->head && buf->head->datalen >= 2); r = parse_socks_client((uint8_t*)buf->head->data, buf->head->datalen, diff --git a/src/or/buffers.h b/src/or/buffers.h index 6d0c68500b..2b43ea14b1 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -101,7 +101,7 @@ void assert_buf_ok(buf_t *buf); #ifdef BUFFERS_PRIVATE STATIC int buf_find_string_offset(const buf_t *buf, const char *s, size_t n); -STATIC void buf_pullup(buf_t *buf, size_t bytes, int nulterminate); +STATIC void buf_pullup(buf_t *buf, size_t bytes); void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz); #define DEBUG_CHUNK_ALLOC diff --git a/src/or/channel.c b/src/or/channel.c index bf0387f10e..5f69a0864b 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -1,9 +1,13 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file channel.c - * \brief OR-to-OR channel abstraction layer + * + * \brief OR/OP-to-OR channel abstraction layer. A channel's job is to + * transfer cells from Tor instance to Tor instance. + * Currently, there is only one implementation of the channel abstraction: in + * channeltls.c. **/ /* @@ -127,13 +131,13 @@ typedef struct channel_idmap_entry_s { TOR_LIST_HEAD(channel_list_s, channel_s) channel_list; } channel_idmap_entry_t; -static INLINE unsigned +static inline unsigned channel_idmap_hash(const channel_idmap_entry_t *ent) { return (unsigned) siphash24g(ent->digest, DIGEST_LEN); } -static INLINE int +static inline int channel_idmap_eq(const channel_idmap_entry_t *a, const channel_idmap_entry_t *b) { @@ -2652,6 +2656,11 @@ channel_process_cells(channel_t *chan) /* * Process cells until we're done or find one we have no current handler * for. + * + * We must free the cells here after calling the handler, since custody + * of the buffer was given to the channel layer when they were queued; + * see comments on memory management in channel_queue_cell() and in + * channel_queue_var_cell() below. */ while (NULL != (q = TOR_SIMPLEQ_FIRST(&chan->incoming_queue))) { tor_assert(q); @@ -2669,6 +2678,7 @@ channel_process_cells(channel_t *chan) q->u.fixed.cell, chan, U64_PRINTF_ARG(chan->global_identifier)); chan->cell_handler(chan, q->u.fixed.cell); + tor_free(q->u.fixed.cell); tor_free(q); } else if (q->type == CELL_QUEUE_VAR && chan->var_cell_handler) { @@ -2681,6 +2691,7 @@ channel_process_cells(channel_t *chan) q->u.var.var_cell, chan, U64_PRINTF_ARG(chan->global_identifier)); chan->var_cell_handler(chan, q->u.var.var_cell); + tor_free(q->u.var.var_cell); tor_free(q); } else { /* Can't handle this one */ @@ -2701,6 +2712,7 @@ channel_queue_cell(channel_t *chan, cell_t *cell) { int need_to_queue = 0; cell_queue_entry_t *q; + cell_t *cell_copy = NULL; tor_assert(chan); tor_assert(cell); @@ -2728,8 +2740,19 @@ channel_queue_cell(channel_t *chan, cell_t *cell) U64_PRINTF_ARG(chan->global_identifier)); chan->cell_handler(chan, cell); } else { - /* Otherwise queue it and then process the queue if possible. */ - q = cell_queue_entry_new_fixed(cell); + /* + * Otherwise queue it and then process the queue if possible. + * + * We queue a copy, not the original pointer - it might have been on the + * stack in connection_or_process_cells_from_inbuf() (or another caller + * if we ever have a subclass other than channel_tls_t), or be freed + * there after we return. This is the uncommon case; the non-copying + * fast path occurs in the if (!need_to_queue) case above when the + * upper layer has installed cell handlers. + */ + cell_copy = tor_malloc_zero(sizeof(cell_t)); + memcpy(cell_copy, cell, sizeof(cell_t)); + q = cell_queue_entry_new_fixed(cell_copy); log_debug(LD_CHANNEL, "Queueing incoming cell_t %p for channel %p " "(global ID " U64_FORMAT ")", @@ -2755,6 +2778,7 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) { int need_to_queue = 0; cell_queue_entry_t *q; + var_cell_t *cell_copy = NULL; tor_assert(chan); tor_assert(var_cell); @@ -2783,8 +2807,18 @@ channel_queue_var_cell(channel_t *chan, var_cell_t *var_cell) U64_PRINTF_ARG(chan->global_identifier)); chan->var_cell_handler(chan, var_cell); } else { - /* Otherwise queue it and then process the queue if possible. */ - q = cell_queue_entry_new_var(var_cell); + /* + * Otherwise queue it and then process the queue if possible. + * + * We queue a copy, not the original pointer - it might have been on the + * stack in connection_or_process_cells_from_inbuf() (or another caller + * if we ever have a subclass other than channel_tls_t), or be freed + * there after we return. This is the uncommon case; the non-copying + * fast path occurs in the if (!need_to_queue) case above when the + * upper layer has installed cell handlers. + */ + cell_copy = var_cell_copy(var_cell); + q = cell_queue_entry_new_var(cell_copy); log_debug(LD_CHANNEL, "Queueing incoming var_cell_t %p for channel %p " "(global ID " U64_FORMAT ")", @@ -2834,7 +2868,7 @@ channel_assert_counter_consistency(void) (n_channel_bytes_in_queues + n_channel_bytes_passed_to_lower_layer)); } -/** DOCDOC */ +/* DOCDOC */ static int is_destroy_cell(channel_t *chan, const cell_queue_entry_t *q, circid_t *circid_out) @@ -3069,6 +3103,7 @@ channel_free_list(smartlist_t *channels, int mark_for_close) if (curr->cmux) { circuitmux_detach_all_circuits(curr->cmux, NULL); } + SMARTLIST_DEL_CURRENT(channels, curr); channel_unregister(curr); if (mark_for_close) { if (!CHANNEL_CONDEMNED(curr)) { @@ -4431,10 +4466,10 @@ channel_num_circuits(channel_t *chan) * This is called when setting up a channel and replaces the old * connection_or_set_circid_type() */ -void -channel_set_circid_type(channel_t *chan, - crypto_pk_t *identity_rcvd, - int consider_identity) +MOCK_IMPL(void, +channel_set_circid_type,(channel_t *chan, + crypto_pk_t *identity_rcvd, + int consider_identity)) { int started_here; crypto_pk_t *our_identity; diff --git a/src/or/channel.h b/src/or/channel.h index ecc2a092e4..129c0c2013 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -182,7 +182,7 @@ struct channel_s { * space should we use? */ circ_id_type_bitfield_t circ_id_type:2; - /** DOCDOC*/ + /* DOCDOC */ unsigned wide_circ_ids:1; /** For how many circuits are we n_chan? What about p_chan? */ @@ -531,7 +531,7 @@ channel_t * channel_next_with_digest(channel_t *chan); CHANNEL_IS_OPEN(chan) || \ CHANNEL_IS_MAINT(chan)) -static INLINE int +static inline int channel_is_in_state(channel_t *chan, channel_state_t state) { return chan->state == state; @@ -562,8 +562,9 @@ int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info); int channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target); unsigned int channel_num_circuits(channel_t *chan); -void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd, - int consider_identity); +MOCK_DECL(void,channel_set_circid_type,(channel_t *chan, + crypto_pk_t *identity_rcvd, + int consider_identity)); void channel_timestamp_client(channel_t *chan); void channel_update_xmit_queue_size(channel_t *chan); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 1cf697ccc5..c65af5d040 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -1,9 +1,11 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file channeltls.c - * \brief channel_t concrete subclass using or_connection_t + * + * \brief A concrete subclass of channel_t using or_connection_t to transfer + * cells between Tor instances. **/ /* @@ -13,6 +15,8 @@ #define TOR_CHANNEL_INTERNAL_ +#define CHANNELTLS_PRIVATE + #include "or.h" #include "channel.h" #include "channeltls.h" @@ -22,6 +26,7 @@ #include "connection.h" #include "connection_or.h" #include "control.h" +#include "link_handshake.h" #include "relay.h" #include "rephist.h" #include "router.h" @@ -48,9 +53,6 @@ uint64_t stats_n_authorize_cells_processed = 0; /** Active listener, if any */ channel_listener_t *channel_tls_listener = NULL; -/* Utility function declarations */ -static void channel_tls_common_init(channel_tls_t *tlschan); - /* channel_tls_t method declarations */ static void channel_tls_close_method(channel_t *chan); @@ -92,12 +94,6 @@ static void channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *tlschan); static void channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *tlschan); -static void channel_tls_process_certs_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_auth_challenge_cell(var_cell_t *cell, - channel_tls_t *tlschan); -static void channel_tls_process_authenticate_cell(var_cell_t *cell, - channel_tls_t *tlschan); static int command_allowed_before_handshake(uint8_t command); static int enter_v3_handshake_with_cell(var_cell_t *cell, channel_tls_t *tlschan); @@ -107,7 +103,7 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell, * and channel_tls_handle_incoming(). */ -static void +STATIC void channel_tls_common_init(channel_tls_t *tlschan) { channel_t *chan; @@ -1015,6 +1011,11 @@ channel_tls_time_process_cell(cell_t *cell, channel_tls_t *chan, int *time, * for cell types specific to the handshake for this transport protocol and * handles them, and queues all other cells to the channel_t layer, which * eventually will hand them off to command.c. + * + * The channel layer itself decides whether the cell should be queued or + * can be handed off immediately to the upper-layer code. It is responsible + * for copying in the case that it queues; we merely pass pointers through + * which we get from connection_or_process_cells_from_inbuf(). */ void @@ -1112,6 +1113,12 @@ channel_tls_handle_cell(cell_t *cell, or_connection_t *conn) * related and live below the channel_t layer, so no variable-length * cells ever get delivered in the current implementation, but I've left * the mechanism in place for future use. + * + * If we were handing them off to the upper layer, the channel_t queueing + * code would be responsible for memory management, and we'd just be passing + * pointers through from connection_or_process_cells_from_inbuf(). That + * caller always frees them after this function returns, so this function + * should never free var_cell. */ void @@ -1669,30 +1676,9 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) #define NETINFO_NOTICE_SKEW 3600 if (labs(apparent_skew) > NETINFO_NOTICE_SKEW && router_get_by_id_digest(chan->conn->identity_digest)) { - char dbuf[64]; - int severity; - /*XXXX be smarter about when everybody says we are skewed. */ - if (router_digest_is_trusted_dir(chan->conn->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_INFO; - format_time_interval(dbuf, sizeof(dbuf), apparent_skew); - log_fn(severity, LD_GENERAL, - "Received NETINFO cell with skewed time from " - "server at %s:%d. It seems that our clock is %s by %s, or " - "that theirs is %s. Tor requires an accurate clock to work: " - "please check your time and date settings.", - chan->conn->base_.address, - (int)(chan->conn->base_.port), - apparent_skew > 0 ? "ahead" : "behind", - dbuf, - apparent_skew > 0 ? "behind" : "ahead"); - if (severity == LOG_WARN) /* only tell the controller if an authority */ - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=OR:%s:%d", - apparent_skew, - chan->conn->base_.address, - chan->conn->base_.port); + int trusted = router_digest_is_trusted_dir(chan->conn->identity_digest); + clock_skew_warning(TO_CONN(chan->conn), apparent_skew, trusted, LD_GENERAL, + "NETINFO cell", "OR"); } /* XXX maybe act on my_apparent_addr, if the source is sufficiently @@ -1747,16 +1733,17 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) * If it's the server side, wait for an AUTHENTICATE cell. */ -static void +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) { - tor_cert_t *link_cert = NULL; - tor_cert_t *id_cert = NULL; - tor_cert_t *auth_cert = NULL; - uint8_t *ptr; +#define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024 + tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1]; int n_certs, i; + certs_cell_t *cc = NULL; + int send_netinfo = 0; + memset(certs, 0, sizeof(certs)); tor_assert(cell); tor_assert(chan); tor_assert(chan->conn); @@ -1786,63 +1773,41 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) if (cell->circ_id) ERR("It had a nonzero circuit ID"); - n_certs = cell->payload[0]; - ptr = cell->payload + 1; + if (certs_cell_parse(&cc, cell->payload, cell->payload_len) < 0) + ERR("It couldn't be parsed."); + + n_certs = cc->n_certs; + for (i = 0; i < n_certs; ++i) { - uint8_t cert_type; - uint16_t cert_len; - if (cell->payload_len < 3) - goto truncated; - if (ptr > cell->payload + cell->payload_len - 3) { - goto truncated; - } - cert_type = *ptr; - cert_len = ntohs(get_uint16(ptr+1)); - if (cell->payload_len < 3 + cert_len) - goto truncated; - if (ptr > cell->payload + cell->payload_len - cert_len - 3) { - goto truncated; - } - if (cert_type == OR_CERT_TYPE_TLS_LINK || - cert_type == OR_CERT_TYPE_ID_1024 || - cert_type == OR_CERT_TYPE_AUTH_1024) { - tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len); - if (!cert) { - log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received undecodable certificate in CERTS cell from %s:%d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + certs_cell_cert_t *c = certs_cell_get_certs(cc, i); + + uint16_t cert_type = c->cert_type; + uint16_t cert_len = c->cert_len; + uint8_t *cert_body = certs_cell_cert_getarray_body(c); + + if (cert_type > MAX_CERT_TYPE_WANTED) + continue; + + tor_x509_cert_t *cert = tor_x509_cert_decode(cert_body, cert_len); + if (!cert) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received undecodable certificate in CERTS cell from %s:%d", + safe_str(chan->conn->base_.address), + chan->conn->base_.port); + } else { + if (certs[cert_type]) { + tor_x509_cert_free(cert); + ERR("Duplicate x509 certificate"); } else { - if (cert_type == OR_CERT_TYPE_TLS_LINK) { - if (link_cert) { - tor_cert_free(cert); - ERR("Too many TLS_LINK certificates"); - } - link_cert = cert; - } else if (cert_type == OR_CERT_TYPE_ID_1024) { - if (id_cert) { - tor_cert_free(cert); - ERR("Too many ID_1024 certificates"); - } - id_cert = cert; - } else if (cert_type == OR_CERT_TYPE_AUTH_1024) { - if (auth_cert) { - tor_cert_free(cert); - ERR("Too many AUTH_1024 certificates"); - } - auth_cert = cert; - } else { - tor_cert_free(cert); - } + certs[cert_type] = cert; } } - ptr += 3 + cert_len; - continue; - - truncated: - ERR("It ends in the middle of a certificate"); } + tor_x509_cert_t *id_cert = certs[OR_CERT_TYPE_ID_1024]; + tor_x509_cert_t *auth_cert = certs[OR_CERT_TYPE_AUTH_1024]; + tor_x509_cert_t *link_cert = certs[OR_CERT_TYPE_TLS_LINK]; + if (chan->conn->handshake_state->started_here) { int severity; if (! (id_cert && link_cert)) @@ -1867,7 +1832,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) chan->conn->handshake_state->authenticated = 1; { - const digests_t *id_digests = tor_cert_get_id_digests(id_cert); + const common_digests_t *id_digests = + tor_x509_cert_get_id_digests(id_cert); crypto_pk_t *identity_rcvd; if (!id_digests) ERR("Couldn't compute digests for key in ID cert"); @@ -1891,7 +1857,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) safe_str(chan->conn->base_.address), chan->conn->base_.port); chan->conn->handshake_state->id_cert = id_cert; - id_cert = NULL; + certs[OR_CERT_TYPE_ID_1024] = NULL; if (!public_server_mode(get_options())) { /* If we initiated the connection and we are not a public server, we @@ -1918,7 +1884,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) chan->conn->handshake_state->id_cert = id_cert; chan->conn->handshake_state->auth_cert = auth_cert; - id_cert = auth_cert = NULL; + certs[OR_CERT_TYPE_ID_1024] = certs[OR_CERT_TYPE_AUTH_1024] = NULL; } chan->conn->handshake_state->received_certs_cell = 1; @@ -1932,9 +1898,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } err: - tor_cert_free(id_cert); - tor_cert_free(link_cert); - tor_cert_free(auth_cert); + for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) { + tor_x509_cert_free(certs[i]); + } + certs_cell_free(cc); #undef ERR } @@ -1949,11 +1916,11 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) * want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell. */ -static void +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) { int n_types, i, use_type = -1; - uint8_t *cp; + auth_challenge_cell_t *ac = NULL; tor_assert(cell); tor_assert(chan); @@ -1966,7 +1933,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) safe_str(chan->conn->base_.address), \ chan->conn->base_.port, (s)); \ connection_or_close_for_error(chan->conn, 0); \ - return; \ + goto done; \ } while (0) if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) @@ -1979,19 +1946,17 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) ERR("We already received one"); if (!(chan->conn->handshake_state->received_certs_cell)) ERR("We haven't gotten a CERTS cell yet"); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2) - ERR("It was too short"); if (cell->circ_id) ERR("It had a nonzero circuit ID"); - n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN)); - if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types) - ERR("It looks truncated"); + if (auth_challenge_cell_parse(&ac, cell->payload, cell->payload_len) < 0) + ERR("It was not well-formed."); + + n_types = ac->n_methods; /* Now see if there is an authentication type we can use */ - cp = cell->payload+OR_AUTH_CHALLENGE_LEN + 2; - for (i = 0; i < n_types; ++i, cp += 2) { - uint16_t authtype = ntohs(get_uint16(cp)); + for (i = 0; i < n_types; ++i) { + uint16_t authtype = auth_challenge_cell_get_methods(ac, i); if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET) use_type = authtype; } @@ -2002,7 +1967,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) /* If we're not a public server then we don't want to authenticate on a connection we originated, and we already sent a NETINFO cell when we got the CERTS cell. We have nothing more to do. */ - return; + goto done; } if (use_type >= 0) { @@ -2016,7 +1981,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) log_warn(LD_OR, "Couldn't send authenticate cell"); connection_or_close_for_error(chan->conn, 0); - return; + goto done; } } else { log_info(LD_OR, @@ -2029,9 +1994,12 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) if (connection_or_send_netinfo(chan->conn) < 0) { log_warn(LD_OR, "Couldn't send netinfo cell"); connection_or_close_for_error(chan->conn, 0); - return; + goto done; } + done: + auth_challenge_cell_free(ac); + #undef ERR } @@ -2045,10 +2013,10 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) * the identity of the router on the other side of the connection. */ -static void +STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) { - uint8_t expected[V3_AUTH_FIXED_PART_LEN]; + uint8_t expected[V3_AUTH_FIXED_PART_LEN+256]; const uint8_t *auth; int authlen; @@ -2104,11 +2072,13 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) if (authlen < V3_AUTH_BODY_LEN + 1) ERR("Authenticator was too short"); - if (connection_or_compute_authenticate_cell_body( - chan->conn, expected, sizeof(expected), NULL, 1) < 0) + ssize_t bodylen = + connection_or_compute_authenticate_cell_body( + chan->conn, expected, sizeof(expected), NULL, 1); + if (bodylen < 0 || bodylen != V3_AUTH_FIXED_PART_LEN) ERR("Couldn't compute expected AUTHENTICATE cell body"); - if (tor_memneq(expected, auth, sizeof(expected))) + if (tor_memneq(expected, auth, bodylen)) ERR("Some field in the AUTHENTICATE cell body was not as expected"); { @@ -2153,8 +2123,8 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) { crypto_pk_t *identity_rcvd = tor_tls_cert_get_key(chan->conn->handshake_state->id_cert); - const digests_t *id_digests = - tor_cert_get_id_digests(chan->conn->handshake_state->id_cert); + const common_digests_t *id_digests = + tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert); /* This must exist; we checked key type when reading the cert. */ tor_assert(id_digests); diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 507429420b..a4d9c7a095 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -52,5 +52,15 @@ void channel_tls_update_marks(or_connection_t *conn); /* Cleanup at shutdown */ void channel_tls_free_all(void); +#ifdef CHANNELTLS_PRIVATE +STATIC void channel_tls_process_certs_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell, + channel_tls_t *tlschan); +STATIC void channel_tls_common_init(channel_tls_t *tlschan); +STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, + channel_tls_t *tlschan); +#endif + #endif diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c index a0115cc6ec..552947eba2 100644 --- a/src/or/circpathbias.c +++ b/src/or/circpathbias.c @@ -1,9 +1,18 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file circpathbias.c + * + * \brief Code to track success/failure rates of circuits built through + * different tor nodes, in an attempt to detect attacks where + * an attacker deliberately causes circuits to fail until the client + * choses a path they like. + */ + #include "or.h" #include "channel.h" #include "circpathbias.h" diff --git a/src/or/circpathbias.h b/src/or/circpathbias.h index 9e973850d5..ce76689d5f 100644 --- a/src/or/circpathbias.h +++ b/src/or/circpathbias.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 946c002735..28d286cd72 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1,12 +1,14 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file circuitbuild.c - * \brief The actual details of building circuits. + * + * \brief Implements the details of building circuits (by chosing paths, + * constructing/sending create/extend cells, and so on). **/ #define CIRCUITBUILD_PRIVATE @@ -493,11 +495,26 @@ circuit_handle_first_hop(origin_circuit_t *circ) int err_reason = 0; const char *msg = NULL; int should_launch = 0; + const or_options_t *options = get_options(); firsthop = onion_next_hop_in_cpath(circ->cpath); tor_assert(firsthop); tor_assert(firsthop->extend_info); + /* Some bridges are on private addresses. Others pass a dummy private + * address to the pluggable transport, which ignores it. + * Deny the connection if: + * - the address is internal, and + * - we're not connecting to a configured bridge, and + * - we're not configured to allow extends to private addresses. */ + if (tor_addr_is_internal(&firsthop->extend_info->addr, 0) && + !extend_info_is_a_configured_bridge(firsthop->extend_info) && + !options->ExtendAllowPrivateAddresses) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Client asked me to connect directly to a private address"); + return -END_CIRC_REASON_TORPROTOCOL; + } + /* now see if we're already connected to the first OR in 'route' */ log_debug(LD_CIRC,"Looking for firsthop '%s'", fmt_addrport(&firsthop->extend_info->addr, @@ -737,7 +754,7 @@ inform_testing_reachability(void) /** Return true iff we should send a create_fast cell to start building a given * circuit */ -static INLINE int +static inline int should_use_create_fast_for_circuit(origin_circuit_t *circ) { const or_options_t *options = get_options(); @@ -967,7 +984,7 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) } control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); clear_broken_connection_map(1); - if (server_mode(options) && !check_whether_orport_reachable()) { + if (server_mode(options) && !check_whether_orport_reachable(options)) { inform_testing_reachability(); consider_testing_reachability(1, 1); } @@ -1053,6 +1070,10 @@ circuit_note_clock_jumped(int seconds_elapsed) "CLOCK_JUMPED"); circuit_mark_all_unused_circs(); circuit_mark_all_dirty_circs_as_unusable(); + if (seconds_elapsed < 0) { + /* Restart all the timers in case we jumped a long way into the past. */ + reset_all_main_loop_timers(); + } } /** Take the 'extend' <b>cell</b>, pull out addr/port plus the onion @@ -1396,9 +1417,12 @@ onionskin_answer(or_circuit_t *circ, log_debug(LD_CIRC,"Finished sending '%s' cell.", circ->is_first_hop ? "created_fast" : "created"); - /* Ignore the local bit when testing - many test networks run on local - * addresses */ - if ((!channel_is_local(circ->p_chan) || get_options()->TestingTorNetwork) + /* Ignore the local bit when ExtendAllowPrivateAddresses is set: + * it violates the assumption that private addresses are local. + * Also, many test networks run on local addresses, and + * TestingTorNetwork sets ExtendAllowPrivateAddresses. */ + if ((!channel_is_local(circ->p_chan) + || get_options()->ExtendAllowPrivateAddresses) && !channel_is_outgoing(circ->p_chan)) { /* record that we could process create cells from a non-local conn * that we didn't initiate; presumably this means that create cells @@ -1753,6 +1777,8 @@ pick_tor2web_rendezvous_node(router_crn_flags_t flags, const node_t *rp_node = NULL; const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0; const int need_desc = (flags & CRN_NEED_DESC) != 0; + const int pref_addr = (flags & CRN_PREF_ADDR) != 0; + const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; smartlist_t *whitelisted_live_rps = smartlist_new(); smartlist_t *all_live_nodes = smartlist_new(); @@ -1763,7 +1789,9 @@ pick_tor2web_rendezvous_node(router_crn_flags_t flags, router_add_running_nodes_to_smartlist(all_live_nodes, allow_invalid, 0, 0, 0, - need_desc); + need_desc, + pref_addr, + direct_conn); /* Filter all_live_nodes to only add live *and* whitelisted RPs to * the list whitelisted_live_rps. */ @@ -2129,7 +2157,10 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) const node_t *choice; smartlist_t *excluded; const or_options_t *options = get_options(); - router_crn_flags_t flags = CRN_NEED_GUARD|CRN_NEED_DESC; + /* If possible, choose an entry server with a preferred address, + * otherwise, choose one with an allowed address */ + router_crn_flags_t flags = (CRN_NEED_GUARD|CRN_NEED_DESC|CRN_PREF_ADDR| + CRN_DIRECT_CONN); const node_t *node; if (state && options->UseEntryGuards && @@ -2146,14 +2177,6 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) * family. */ nodelist_add_node_and_family(excluded, node); } - if (firewall_is_fascist_or()) { - /* Exclude all ORs that we can't reach through our firewall */ - smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, const node_t *, node, { - if (!fascist_firewall_allows_node(node)) - smartlist_add(excluded, (void*)node); - }); - } /* and exclude current entry guards and their families, * unless we're in a test network, and excluding guards * would exclude all nodes (i.e. we're in an incredibly small tor network, @@ -2232,9 +2255,11 @@ onion_extend_cpath(origin_circuit_t *circ) if (r) { /* If we're a client, use the preferred address rather than the primary address, for potentially connecting to an IPv6 OR - port. */ - info = extend_info_from_node(r, server_mode(get_options()) == 0); - tor_assert(info); + port. Servers always want the primary (IPv4) address. */ + int client = (server_mode(get_options()) == 0); + info = extend_info_from_node(r, client); + /* Clients can fail to find an allowed address */ + tor_assert(info || client); } } else { const node_t *r = @@ -2309,33 +2334,43 @@ extend_info_new(const char *nickname, const char *digest, * <b>for_direct_connect</b> is true, in which case the preferred * address is used instead. May return NULL if there is not enough * info about <b>node</b> to extend to it--for example, if there is no - * routerinfo_t or microdesc_t. + * routerinfo_t or microdesc_t, or if for_direct_connect is true and none of + * the node's addresses are allowed by tor's firewall and IP version config. **/ extend_info_t * extend_info_from_node(const node_t *node, int for_direct_connect) { tor_addr_port_t ap; + int valid_addr = 0; if (node->ri == NULL && (node->rs == NULL || node->md == NULL)) return NULL; + /* Choose a preferred address first, but fall back to an allowed address. + * choose_address returns 1 on success, but get_prim_orport returns 0. */ if (for_direct_connect) - node_get_pref_orport(node, &ap); + valid_addr = fascist_firewall_choose_address_node(node, + FIREWALL_OR_CONNECTION, + 0, &ap); else - node_get_prim_orport(node, &ap); + valid_addr = !node_get_prim_orport(node, &ap); - log_debug(LD_CIRC, "using %s for %s", - fmt_addrport(&ap.addr, ap.port), - node->ri ? node->ri->nickname : node->rs->nickname); + if (valid_addr) + log_debug(LD_CIRC, "using %s for %s", + fmt_addrport(&ap.addr, ap.port), + node->ri ? node->ri->nickname : node->rs->nickname); + else + log_warn(LD_CIRC, "Could not choose valid address for %s", + node->ri ? node->ri->nickname : node->rs->nickname); - if (node->ri) + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, node->ri->onion_pkey, node->ri->onion_curve25519_pkey, &ap.addr, ap.port); - else if (node->rs && node->md) + else if (valid_addr && node->rs && node->md) return extend_info_new(node->rs->nickname, node->identity, node->md->onion_pkey, @@ -2396,3 +2431,20 @@ build_state_get_exit_nickname(cpath_build_state_t *state) return state->chosen_exit->nickname; } +/** Return true iff the given address can be used to extend to. */ +int +extend_info_addr_is_allowed(const tor_addr_t *addr) +{ + tor_assert(addr); + + /* Check if we have a private address and if we can extend to it. */ + if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) && + !get_options()->ExtendAllowPrivateAddresses) { + goto disallow; + } + /* Allowed! */ + return 1; + disallow: + return 0; +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 01563791b7..7f5fd511a9 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -53,6 +53,7 @@ extend_info_t *extend_info_new(const char *nickname, const char *digest, extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); extend_info_t *extend_info_dup(extend_info_t *info); void extend_info_free(extend_info_t *info); +int extend_info_addr_is_allowed(const tor_addr_t *addr); const node_t *build_state_get_exit_node(cpath_build_state_t *state); const char *build_state_get_exit_nickname(cpath_build_state_t *state); diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index bf7f8daca7..d7dbfe5744 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1,12 +1,13 @@ /* Copyright 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file circuitlist.c - * \brief Manage the global circuit list. + * + * \brief Manage the global circuit list, and looking up circuits within it. **/ #define CIRCUITLIST_PRIVATE #include "or.h" @@ -44,11 +45,17 @@ static smartlist_t *global_circuitlist = NULL; /** A list of all the circuits in CIRCUIT_STATE_CHAN_WAIT. */ static smartlist_t *circuits_pending_chans = NULL; +/** A list of all the circuits that have been marked with + * circuit_mark_for_close and which are waiting for circuit_about_to_free. */ +static smartlist_t *circuits_pending_close = NULL; + static void circuit_free_cpath_node(crypt_path_t *victim); static void cpath_ref_decref(crypt_path_reference_t *cpath_ref); //static void circuit_set_rend_token(or_circuit_t *circ, int is_rend_circ, // const uint8_t *token); static void circuit_clear_rend_token(or_circuit_t *circ); +static void circuit_about_to_free_atexit(circuit_t *circ); +static void circuit_about_to_free(circuit_t *circ); /********* END VARIABLES ************/ @@ -66,7 +73,7 @@ typedef struct chan_circid_circuit_map_t { /** Helper for hash tables: compare the channel and circuit ID for a and * b, and return less than, equal to, or greater than zero appropriately. */ -static INLINE int +static inline int chan_circid_entries_eq_(chan_circid_circuit_map_t *a, chan_circid_circuit_map_t *b) { @@ -75,7 +82,7 @@ chan_circid_entries_eq_(chan_circid_circuit_map_t *a, /** Helper: return a hash based on circuit ID and the pointer value of * chan in <b>a</b>. */ -static INLINE unsigned int +static inline unsigned int chan_circid_entry_hash_(chan_circid_circuit_map_t *a) { /* Try to squeze the siphash input into 8 bytes to save any extra siphash @@ -451,16 +458,27 @@ circuit_count_pending_on_channel(channel_t *chan) void circuit_close_all_marked(void) { + if (circuits_pending_close == NULL) + return; + smartlist_t *lst = circuit_get_global_list(); - SMARTLIST_FOREACH_BEGIN(lst, circuit_t *, circ) { - /* Fix up index if SMARTLIST_DEL_CURRENT just moved this one. */ - circ->global_circuitlist_idx = circ_sl_idx; - if (circ->marked_for_close) { - circ->global_circuitlist_idx = -1; - circuit_free(circ); - SMARTLIST_DEL_CURRENT(lst, circ); + SMARTLIST_FOREACH_BEGIN(circuits_pending_close, circuit_t *, circ) { + tor_assert(circ->marked_for_close); + + /* Remove it from the circuit list. */ + int idx = circ->global_circuitlist_idx; + smartlist_del(lst, idx); + if (idx < smartlist_len(lst)) { + circuit_t *replacement = smartlist_get(lst, idx); + replacement->global_circuitlist_idx = idx; } + circ->global_circuitlist_idx = -1; + + circuit_about_to_free(circ); + circuit_free(circ); } SMARTLIST_FOREACH_END(circ); + + smartlist_clear(circuits_pending_close); } /** Return the head of the global linked list of circuits. */ @@ -716,8 +734,8 @@ origin_circuit_new(void) return circ; } -/** Allocate a new or_circuit_t, connected to <b>p_conn</b> as - * <b>p_circ_id</b>. If <b>p_conn</b> is NULL, the circuit is unattached. */ +/** Allocate a new or_circuit_t, connected to <b>p_chan</b> as + * <b>p_circ_id</b>. If <b>p_chan</b> is NULL, the circuit is unattached. */ or_circuit_t * or_circuit_new(circid_t p_circ_id, channel_t *p_chan) { @@ -738,6 +756,18 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) return circ; } +/** Free all storage held in circ->testing_cell_stats */ +void +circuit_clear_testing_cell_stats(circuit_t *circ) +{ + if (!circ || !circ->testing_cell_stats) + return; + SMARTLIST_FOREACH(circ->testing_cell_stats, testing_cell_stats_entry_t *, + ent, tor_free(ent)); + smartlist_free(circ->testing_cell_stats); + circ->testing_cell_stats = NULL; +} + /** Deallocate space associated with circ. */ STATIC void @@ -749,6 +779,8 @@ circuit_free(circuit_t *circ) if (!circ) return; + circuit_clear_testing_cell_stats(circ); + if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); mem = ocirc; @@ -885,6 +917,7 @@ circuit_free_all(void) } } tmp->global_circuitlist_idx = -1; + circuit_about_to_free_atexit(tmp); circuit_free(tmp); SMARTLIST_DEL_CURRENT(lst, tmp); } SMARTLIST_FOREACH_END(tmp); @@ -895,6 +928,9 @@ circuit_free_all(void) smartlist_free(circuits_pending_chans); circuits_pending_chans = NULL; + smartlist_free(circuits_pending_close); + circuits_pending_close = NULL; + { chan_circid_circuit_map_t **elt, **next, *c; for (elt = HT_START(chan_circid_map, &chan_circid_map); @@ -1030,7 +1066,7 @@ circuit_get_by_global_id(uint32_t id) * If <b>found_entry_out</b> is provided, set it to true if we have a * placeholder entry for circid/chan, and leave it unset otherwise. */ -static INLINE circuit_t * +static inline circuit_t * circuit_get_by_circid_channel_impl(circid_t circ_id, channel_t *chan, int *found_entry_out) { @@ -1703,6 +1739,65 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line, reason = END_CIRC_REASON_NONE; } + circ->marked_for_close = line; + circ->marked_for_close_file = file; + circ->marked_for_close_reason = reason; + circ->marked_for_close_orig_reason = orig_reason; + + if (!CIRCUIT_IS_ORIGIN(circ)) { + or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); + if (or_circ->rend_splice) { + if (!or_circ->rend_splice->base_.marked_for_close) { + /* do this after marking this circuit, to avoid infinite recursion. */ + circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); + } + or_circ->rend_splice = NULL; + } + } + + if (circuits_pending_close == NULL) + circuits_pending_close = smartlist_new(); + + smartlist_add(circuits_pending_close, circ); +} + +/** Called immediately before freeing a marked circuit <b>circ</b> from + * circuit_free_all() while shutting down Tor; this is a safe-at-shutdown + * version of circuit_about_to_free(). It's important that it at least + * do circuitmux_detach_circuit() when appropriate. + */ +static void +circuit_about_to_free_atexit(circuit_t *circ) +{ + + if (circ->n_chan) { + circuit_clear_cell_queue(circ, circ->n_chan); + circuitmux_detach_circuit(circ->n_chan->cmux, circ); + circuit_set_n_circid_chan(circ, 0, NULL); + } + + if (! CIRCUIT_IS_ORIGIN(circ)) { + or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); + + if (or_circ->p_chan) { + circuit_clear_cell_queue(circ, or_circ->p_chan); + circuitmux_detach_circuit(or_circ->p_chan->cmux, circ); + circuit_set_p_circid_chan(or_circ, 0, NULL); + } + } +} + +/** Called immediately before freeing a marked circuit <b>circ</b>. + * Disconnects the circuit from other data structures, launches events + * as appropriate, and performs other housekeeping. + */ +static void +circuit_about_to_free(circuit_t *circ) +{ + + int reason = circ->marked_for_close_reason; + int orig_reason = circ->marked_for_close_orig_reason; + if (circ->state == CIRCUIT_STATE_ONIONSKIN_PENDING) { onion_pending_remove(TO_OR_CIRCUIT(circ)); } @@ -1726,6 +1821,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line, (circ->state == CIRCUIT_STATE_OPEN)?CIRC_EVENT_CLOSED:CIRC_EVENT_FAILED, orig_reason); } + if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ); int timed_out = (reason == END_CIRC_REASON_TIMEOUT); @@ -1810,20 +1906,6 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line, connection_edge_destroy(circ->n_circ_id, conn); ocirc->p_streams = NULL; } - - circ->marked_for_close = line; - circ->marked_for_close_file = file; - - if (!CIRCUIT_IS_ORIGIN(circ)) { - or_circuit_t *or_circ = TO_OR_CIRCUIT(circ); - if (or_circ->rend_splice) { - if (!or_circ->rend_splice->base_.marked_for_close) { - /* do this after marking this circuit, to avoid infinite recursion. */ - circuit_mark_for_close(TO_CIRCUIT(or_circ->rend_splice), reason); - } - or_circ->rend_splice = NULL; - } - } } /** Given a marked circuit <b>circ</b>, aggressively free its cell queues to @@ -1836,8 +1918,14 @@ marked_circuit_free_cells(circuit_t *circ) return; } cell_queue_clear(&circ->n_chan_cells); - if (! CIRCUIT_IS_ORIGIN(circ)) - cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_chan_cells); + if (circ->n_mux) + circuitmux_clear_num_cells(circ->n_mux, circ); + if (! CIRCUIT_IS_ORIGIN(circ)) { + or_circuit_t *orcirc = TO_OR_CIRCUIT(circ); + cell_queue_clear(&orcirc->p_chan_cells); + if (orcirc->p_mux) + circuitmux_clear_num_cells(orcirc->p_mux, circ); + } } static size_t diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h index 4e600da57d..2707b426ab 100644 --- a/src/or/circuitlist.h +++ b/src/or/circuitlist.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -71,6 +71,8 @@ void assert_circuit_ok(const circuit_t *c); void circuit_free_all(void); void circuits_handle_oom(size_t current_allocation); +void circuit_clear_testing_cell_stats(circuit_t *circ); + void channel_note_destroy_pending(channel_t *chan, circid_t id); MOCK_DECL(void, channel_note_destroy_not_pending, (channel_t *chan, circid_t id)); diff --git a/src/or/circuitmux.c b/src/or/circuitmux.c index a77bffac90..cc1c4cd401 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -186,10 +186,10 @@ struct chanid_circid_muxinfo_t { * Static function declarations */ -static INLINE int +static inline int chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, chanid_circid_muxinfo_t *b); -static INLINE unsigned int +static inline unsigned int chanid_circid_entry_hash(chanid_circid_muxinfo_t *a); static chanid_circid_muxinfo_t * circuitmux_find_map_entry(circuitmux_t *cmux, circuit_t *circ); @@ -199,12 +199,12 @@ circuitmux_make_circuit_active(circuitmux_t *cmux, circuit_t *circ, static void circuitmux_make_circuit_inactive(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction); -static INLINE void +static inline void circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction); -static INLINE circuit_t ** +static inline circuit_t ** circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ); -static INLINE circuit_t ** +static inline circuit_t ** circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ); static void circuitmux_assert_okay_pass_one(circuitmux_t *cmux); static void circuitmux_assert_okay_pass_two(circuitmux_t *cmux); @@ -226,7 +226,7 @@ static int64_t global_destroy_ctr = 0; * used by circuitmux_notify_xmit_cells(). */ -static INLINE void +static inline void circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, cell_direction_t direction) { @@ -306,7 +306,7 @@ circuitmux_move_active_circ_to_tail(circuitmux_t *cmux, circuit_t *circ, circuitmux_assert_okay_paranoid(cmux); } -static INLINE circuit_t ** +static inline circuit_t ** circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ) { tor_assert(cmux); @@ -319,7 +319,7 @@ circuitmux_next_active_circ_p(circuitmux_t *cmux, circuit_t *circ) } } -static INLINE circuit_t ** +static inline circuit_t ** circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ) { tor_assert(cmux); @@ -338,7 +338,7 @@ circuitmux_prev_active_circ_p(circuitmux_t *cmux, circuit_t *circ) * than zero appropriately. */ -static INLINE int +static inline int chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, chanid_circid_muxinfo_t *b) { @@ -349,7 +349,7 @@ chanid_circid_entries_eq(chanid_circid_muxinfo_t *a, * Helper: return a hash based on circuit ID and channel ID in a. */ -static INLINE unsigned int +static inline unsigned int chanid_circid_entry_hash(chanid_circid_muxinfo_t *a) { return (((unsigned int)(a->circ_id) << 8) ^ diff --git a/src/or/circuitmux.h b/src/or/circuitmux.h index 837e3961bf..00745ac4a1 100644 --- a/src/or/circuitmux.h +++ b/src/or/circuitmux.h @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index 1c0318de06..b784a140ac 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -115,7 +115,7 @@ TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *); * if the cast is impossible. */ -static INLINE ewma_policy_data_t * +static inline ewma_policy_data_t * TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol) { if (!pol) return NULL; @@ -130,7 +130,7 @@ TO_EWMA_POL_DATA(circuitmux_policy_data_t *pol) * and assert if the cast is impossible. */ -static INLINE ewma_policy_circ_data_t * +static inline ewma_policy_circ_data_t * TO_EWMA_POL_CIRC_DATA(circuitmux_policy_circ_data_t *pol) { if (!pol) return NULL; @@ -147,7 +147,7 @@ static int compare_cell_ewma_counts(const void *p1, const void *p2); static unsigned cell_ewma_tick_from_timeval(const struct timeval *now, double *remainder_out); static circuit_t * cell_ewma_to_circuit(cell_ewma_t *ewma); -static INLINE double get_scale_factor(unsigned from_tick, unsigned to_tick); +static inline double get_scale_factor(unsigned from_tick, unsigned to_tick); static cell_ewma_t * pop_first_cell_ewma(ewma_policy_data_t *pol); static void remove_cell_ewma(ewma_policy_data_t *pol, cell_ewma_t *ewma); static void scale_single_cell_ewma(cell_ewma_t *ewma, unsigned cur_tick); @@ -644,7 +644,7 @@ cell_ewma_set_scale_factor(const or_options_t *options, /** Return the multiplier necessary to convert the value of a cell sent in * 'from_tick' to one sent in 'to_tick'. */ -static INLINE double +static inline double get_scale_factor(unsigned from_tick, unsigned to_tick) { /* This math can wrap around, but that's okay: unsigned overflow is diff --git a/src/or/circuitmux_ewma.h b/src/or/circuitmux_ewma.h index 3feef834dd..58aac1e196 100644 --- a/src/or/circuitmux_ewma.h +++ b/src/or/circuitmux_ewma.h @@ -1,4 +1,4 @@ -/* * Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index 7b3ad56537..9ac2d565b5 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -1,9 +1,16 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file circuitstats.c + * + * \brief Maintains and analyzes statistics about circuit built times, so we + * can tell how long we may need to wait for a fast circuit to be constructed. + */ + #define CIRCUITSTATS_PRIVATE #include "or.h" @@ -1232,6 +1239,9 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt) } cbt->liveness.network_last_live = now; cbt->liveness.nonlive_timeouts = 0; + + /* Tell control.c */ + control_event_network_liveness_update(1); } /** @@ -1316,6 +1326,9 @@ circuit_build_times_network_close(circuit_build_times_t *cbt, "Tor has not observed any network activity for the past %d " "seconds. Disabling circuit build timeout recording.", (int)(now - cbt->liveness.network_last_live)); + + /* Tell control.c */ + control_event_network_liveness_update(0); } else { log_info(LD_CIRC, "Got non-live timeout. Current count is: %d", diff --git a/src/or/circuitstats.h b/src/or/circuitstats.h index fe05a24e97..72b160983f 100644 --- a/src/or/circuitstats.h +++ b/src/or/circuitstats.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 3b6f982666..2c724dee05 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -92,7 +92,7 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, /* decide if this circ is suitable for this conn */ /* for rend circs, circ->cpath->prev is not the last router in the - * circuit, it's the magical extra bob hop. so just check the nickname + * circuit, it's the magical extra service hop. so just check the nickname * of the one we meant to finish at. */ build_state = origin_circ->build_state; @@ -1123,12 +1123,12 @@ circuit_build_needed_circs(time_t now) * don't require an exit circuit, review in #13814. * This allows HSs to function in a consensus without exits. */ if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) - connection_ap_attach_pending(); + connection_ap_rescan_and_attach_pending(); /* make sure any hidden services have enough intro points * HS intro point streams only require an internal circuit */ if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) - rend_services_introduce(); + rend_consider_services_intro_points(); circuit_expire_old_circs_as_needed(now); @@ -1189,17 +1189,31 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn) if (CIRCUIT_IS_ORIGIN(circ)) { origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ); + int removed = 0; if (conn == origin_circ->p_streams) { origin_circ->p_streams = conn->next_stream; - return; + removed = 1; + } else { + for (prevconn = origin_circ->p_streams; + prevconn && prevconn->next_stream && prevconn->next_stream != conn; + prevconn = prevconn->next_stream) + ; + if (prevconn && prevconn->next_stream) { + prevconn->next_stream = conn->next_stream; + removed = 1; + } } + if (removed) { + log_debug(LD_APP, "Removing stream %d from circ %u", + conn->stream_id, (unsigned)circ->n_circ_id); - for (prevconn = origin_circ->p_streams; - prevconn && prevconn->next_stream && prevconn->next_stream != conn; - prevconn = prevconn->next_stream) - ; - if (prevconn && prevconn->next_stream) { - prevconn->next_stream = conn->next_stream; + /* If the stream was removed, and it was a rend stream, decrement the + * number of streams on the circuit associated with the rend service. + */ + if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) { + tor_assert(origin_circ->rend_data); + origin_circ->rend_data->nr_streams--; + } return; } } else { @@ -1412,7 +1426,7 @@ static void circuit_testing_opened(origin_circuit_t *circ) { if (have_performed_bandwidth_test || - !check_whether_orport_reachable()) { + !check_whether_orport_reachable(get_options())) { /* either we've already done everything we want with testing circuits, * or this testing circuit became open due to a fluke, e.g. we picked * a last hop where we already had the connection open due to an @@ -1429,7 +1443,8 @@ circuit_testing_opened(origin_circuit_t *circ) static void circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) { - if (server_mode(get_options()) && check_whether_orport_reachable()) + const or_options_t *options = get_options(); + if (server_mode(options) && check_whether_orport_reachable(options)) return; log_info(LD_GENERAL, @@ -1461,7 +1476,7 @@ circuit_has_opened(origin_circuit_t *circ) case CIRCUIT_PURPOSE_C_ESTABLISH_REND: rend_client_rendcirc_has_opened(circ); /* Start building an intro circ if we don't have one yet. */ - connection_ap_attach_pending(); + connection_ap_attach_pending(1); /* This isn't a call to circuit_try_attaching_streams because a * circuit in _C_ESTABLISH_REND state isn't connected to its * hidden service yet, thus we can't attach streams to it yet, @@ -1479,11 +1494,11 @@ circuit_has_opened(origin_circuit_t *circ) circuit_try_attaching_streams(circ); break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ + /* at the service, waiting for introductions */ rend_service_intro_has_opened(circ); break; case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ + /* at the service, connecting to rend point */ rend_service_rendezvous_has_opened(circ); break; case CIRCUIT_PURPOSE_TESTING: @@ -1523,14 +1538,14 @@ void circuit_try_attaching_streams(origin_circuit_t *circ) { /* Attach streams to this circuit if we can. */ - connection_ap_attach_pending(); + connection_ap_attach_pending(1); /* The call to circuit_try_clearing_isolation_state here will do * nothing and return 0 if we didn't attach any streams to circ * above. */ if (circuit_try_clearing_isolation_state(circ)) { /* Maybe *now* we can attach some streams to this circuit. */ - connection_ap_attach_pending(); + connection_ap_attach_pending(1); } } @@ -1603,32 +1618,32 @@ circuit_build_failed(origin_circuit_t *circ) circuit_testing_failed(circ, failed_at_last_hop); break; case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: - /* at Bob, waiting for introductions */ + /* at the service, waiting for introductions */ if (circ->base_.state != CIRCUIT_STATE_OPEN) { circuit_increment_failure_count(); } - /* no need to care here, because bob will rebuild intro + /* no need to care here, because the service will rebuild intro * points periodically. */ break; case CIRCUIT_PURPOSE_C_INTRODUCING: - /* at Alice, connecting to intro point */ - /* Don't increment failure count, since Bob may have picked + /* at the client, connecting to intro point */ + /* Don't increment failure count, since the service may have picked * the introduction point maliciously */ - /* Alice will pick a new intro point when this one dies, if + /* The client will pick a new intro point when this one dies, if * the stream in question still cares. No need to act here. */ break; case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - /* at Alice, waiting for Bob */ + /* at the client, waiting for the service */ circuit_increment_failure_count(); - /* Alice will pick a new rend point when this one dies, if + /* the client will pick a new rend point when this one dies, if * the stream in question still cares. No need to act here. */ break; case CIRCUIT_PURPOSE_S_CONNECT_REND: - /* at Bob, connecting to rend point */ - /* Don't increment failure count, since Alice may have picked + /* at the service, connecting to rend point */ + /* Don't increment failure count, since the client may have picked * the rendezvous point maliciously */ log_info(LD_REND, - "Couldn't connect to Alice's chosen rend point %s " + "Couldn't connect to the client's chosen rend point %s " "(%s hop failed).", escaped(build_state_get_exit_nickname(circ->build_state)), failed_at_last_hop?"last":"non-last"); @@ -1660,7 +1675,11 @@ circuit_launch(uint8_t purpose, int flags) return circuit_launch_by_extend_info(purpose, NULL, flags); } -/** DOCDOC */ +/* Do we have enough descriptors to build paths? + * If need_exit is true, return 1 if we can build exit paths. + * (We need at least one Exit in the consensus to build exit paths.) + * If need_exit is false, return 1 if we can build internal paths. + */ static int have_enough_path_info(int need_exit) { @@ -1755,12 +1774,12 @@ circuit_launch_by_extend_info(uint8_t purpose, switch (purpose) { case CIRCUIT_PURPOSE_C_ESTABLISH_REND: - case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* it's ready right now */ break; case CIRCUIT_PURPOSE_C_INTRODUCING: case CIRCUIT_PURPOSE_S_CONNECT_REND: case CIRCUIT_PURPOSE_C_GENERAL: + case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO: /* need to add a new hop */ tor_assert(extend_info); if (circuit_extend_to_new_exit(circ, extend_info) < 0) @@ -1832,6 +1851,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, tor_assert(conn); tor_assert(circp); + if (ENTRY_TO_CONN(conn)->state != AP_CONN_STATE_CIRCUIT_WAIT) { + connection_t *c = ENTRY_TO_CONN(conn); + log_err(LD_BUG, "Connection state mismatch: wanted " + "AP_CONN_STATE_CIRCUIT_WAIT, but got %d (%s)", + c->state, conn_state_to_string(c->type, c->state)); + } tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT); check_exit_policy = conn->socks_request->command == SOCKS_COMMAND_CONNECT && @@ -1966,6 +1991,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, "No intro points for '%s': re-fetching service descriptor.", safe_str_client(rend_data->onion_address)); rend_client_refetch_v2_renddesc(rend_data); + connection_ap_mark_as_non_pending_circuit(conn); ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_RENDDESC_WAIT; return 0; } @@ -1985,8 +2011,13 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, if (r && node_has_descriptor(r)) { /* We might want to connect to an IPv6 bridge for loading descriptors so we use the preferred address rather than - the primary. */ + the primary. */ extend_info = extend_info_from_node(r, conn->want_onehop ? 1 : 0); + if (!extend_info) { + log_warn(LD_CIRC,"Could not make a one-hop connection to %s. " + "Discarding this circuit.", conn->chosen_exit_name); + return -1; + } } else { log_debug(LD_DIR, "considering %d, %s", want_onehop, conn->chosen_exit_name); @@ -2149,7 +2180,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ, * that an attempt to connect to a hidden service just * succeeded. Tell rendclient.c. */ rend_client_note_connection_attempt_ended( - ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address); + ENTRY_TO_EDGE_CONN(apconn)->rend_data); } if (cpath) { /* we were given one; use it */ @@ -2220,7 +2251,7 @@ consider_recording_trackhost(const entry_connection_t *conn, char fp[HEX_DIGEST_LEN+1]; /* Search the addressmap for this conn's destination. */ - /* If he's not in the address map.. */ + /* If they're not in the address map.. */ if (!options->TrackHostExits || addressmap_have_mapping(conn->socks_request->address, options->TrackHostExitsExpire)) @@ -2264,8 +2295,15 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - if (!circ->base_.timestamp_dirty) - circ->base_.timestamp_dirty = time(NULL); + if (!circ->base_.timestamp_dirty || + ((conn->entry_cfg.isolation_flags & ISO_SOCKSAUTH) && + (conn->entry_cfg.socks_iso_keep_alive) && + (conn->socks_request->usernamelen || + conn->socks_request->passwordlen))) { + /* When stream isolation is in use and controlled by an application + * we are willing to keep using the stream. */ + circ->base_.timestamp_dirty = approx_time(); + } pathbias_count_use_attempt(circ); @@ -2322,6 +2360,25 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) /* we're a general conn */ origin_circuit_t *circ=NULL; + /* Are we linked to a dir conn that aims to fetch a consensus? + * We check here because this conn might no longer be needed. */ + if (base_conn->linked_conn && + base_conn->linked_conn->type == CONN_TYPE_DIR && + base_conn->linked_conn->purpose == DIR_PURPOSE_FETCH_CONSENSUS) { + + /* Yes we are. Is there a consensus fetch farther along than us? */ + if (networkstatus_consensus_is_already_downloading( + TO_DIR_CONN(base_conn->linked_conn)->requested_resource)) { + /* We're doing the "multiple consensus fetch attempts" game from + * proposal 210, and we're late to the party. Just close this conn. + * The circuit and TLS conn that we made will time out after a while + * if nothing else wants to use them. */ + log_info(LD_DIR, "Closing extra consensus fetch (to %s) since one " + "is already downloading.", base_conn->linked_conn->address); + return -1; + } + } + if (conn->chosen_exit_name) { const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); int opt = conn->chosen_exit_optional; diff --git a/src/or/circuituse.h b/src/or/circuituse.h index a59f478ac8..5973978c45 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/command.c b/src/or/command.c index 719b10736b..5ad92bed1e 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -340,7 +340,6 @@ command_process_create_cell(cell_t *cell, channel_t *chan) if (len < 0) { log_warn(LD_OR,"Failed to generate key material. Closing."); circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - tor_free(create_cell); return; } created_cell.cell_type = CELL_CREATED_FAST; diff --git a/src/or/command.h b/src/or/command.h index bea96261bb..12cda6a463 100644 --- a/src/or/command.h +++ b/src/or/command.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/config.c b/src/or/config.c index 89b6087814..4b065a0053 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -162,6 +162,7 @@ static config_var_t option_vars_[] = { V(AuthDirInvalidCCs, CSV, ""), V(AuthDirFastGuarantee, MEMUNIT, "100 KB"), V(AuthDirGuardBWGuarantee, MEMUNIT, "2 MB"), + V(AuthDirPinKeys, BOOL, "0"), V(AuthDirReject, LINELIST, NULL), V(AuthDirRejectCCs, CSV, ""), OBSOLETE("AuthDirRejectUnlisted"), @@ -189,10 +190,12 @@ static config_var_t option_vars_[] = { V(CircuitPriorityHalflife, DOUBLE, "-100.0"), /*negative:'Use default'*/ V(ClientDNSRejectInternalAddresses, BOOL,"1"), V(ClientOnly, BOOL, "0"), - V(ClientPreferIPv6ORPort, BOOL, "0"), + V(ClientPreferIPv6ORPort, AUTOBOOL, "auto"), + V(ClientPreferIPv6DirPort, AUTOBOOL, "auto"), V(ClientRejectInternalAddresses, BOOL, "1"), V(ClientTransportPlugin, LINELIST, NULL), V(ClientUseIPv6, BOOL, "0"), + V(ClientUseIPv4, BOOL, "1"), V(ConsensusParams, STRING, NULL), V(ConnLimit, UINT, "1000"), V(ConnDirectionStatistics, BOOL, "0"), @@ -211,6 +214,7 @@ static config_var_t option_vars_[] = { V(CookieAuthFile, STRING, NULL), V(CountPrivateBandwidth, BOOL, "0"), V(DataDirectory, FILENAME, NULL), + V(DataDirectoryGroupReadable, BOOL, "0"), V(DisableNetwork, BOOL, "0"), V(DirAllowPrivateAddresses, BOOL, "0"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), @@ -220,12 +224,13 @@ static config_var_t option_vars_[] = { V(DirPortFrontPage, FILENAME, NULL), VAR("DirReqStatistics", BOOL, DirReqStatistics_option, "1"), VAR("DirAuthority", LINELIST, DirAuthorities, NULL), + V(DirCache, BOOL, "1"), V(DirAuthorityFallbackRate, DOUBLE, "1.0"), V(DisableAllSwap, BOOL, "0"), V(DisableDebuggerAttachment, BOOL, "1"), V(DisableIOCP, BOOL, "1"), OBSOLETE("DisableV2DirectoryInfo_"), - V(DynamicDHGroups, BOOL, "0"), + OBSOLETE("DynamicDHGroups"), VPORT(DNSPort, LINELIST, NULL), V(DNSListenAddress, LINELIST, NULL), V(DownloadExtraInfo, BOOL, "0"), @@ -250,6 +255,7 @@ static config_var_t option_vars_[] = { V(ExtORPortCookieAuthFileGroupReadable, BOOL, "0"), V(ExtraInfoStatistics, BOOL, "1"), V(FallbackDir, LINELIST, NULL), + V(UseDefaultFallbackDirs, BOOL, "1"), OBSOLETE("FallbackNetworkstatusFile"), V(FascistFirewall, BOOL, "0"), @@ -278,7 +284,7 @@ static config_var_t option_vars_[] = { V(AccelName, STRING, NULL), V(AccelDir, FILENAME, NULL), V(HashedControlPassword, LINELIST, NULL), - V(HidServDirectoryV2, BOOL, "1"), + OBSOLETE("HidServDirectoryV2"), VAR("HiddenServiceDir", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceDirGroupReadable", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines, NULL), @@ -286,7 +292,10 @@ static config_var_t option_vars_[] = { VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL), VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL), - V(HiddenServiceStatistics, BOOL, "0"), + VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL), + V(HiddenServiceStatistics, BOOL, "1"), V(HidServAuth, LINELIST, NULL), V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), @@ -298,15 +307,18 @@ static config_var_t option_vars_[] = { VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL), V(ServerTransportListenAddr, LINELIST, NULL), V(ServerTransportOptions, LINELIST, NULL), + V(SigningKeyLifetime, INTERVAL, "30 days"), V(Socks4Proxy, STRING, NULL), V(Socks5Proxy, STRING, NULL), V(Socks5ProxyUsername, STRING, NULL), V(Socks5ProxyPassword, STRING, NULL), V(KeepalivePeriod, INTERVAL, "5 minutes"), + V(KeepBindCapabilities, AUTOBOOL, "auto"), VAR("Log", LINELIST, Logs, NULL), V(LogMessageDomains, BOOL, "0"), V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), + V(SyslogIdentityTag, STRING, NULL), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@ -329,6 +341,7 @@ static config_var_t option_vars_[] = { V(NumCPUs, UINT, "0"), V(NumDirectoryGuards, UINT, "0"), V(NumEntryGuards, UINT, "0"), + V(OfflineMasterKey, BOOL, "0"), V(ORListenAddress, LINELIST, NULL), VPORT(ORPort, LINELIST, NULL), V(OutboundBindAddress, LINELIST, NULL), @@ -356,6 +369,13 @@ static config_var_t option_vars_[] = { V(TestingTorNetwork, BOOL, "0"), V(TestingMinExitFlagThreshold, MEMUNIT, "0"), V(TestingMinFastFlagThreshold, MEMUNIT, "0"), + + V(TestingLinkCertLifetime, INTERVAL, "2 days"), + V(TestingAuthKeyLifetime, INTERVAL, "2 days"), + V(TestingLinkKeySlop, INTERVAL, "3 hours"), + V(TestingAuthKeySlop, INTERVAL, "3 hours"), + V(TestingSigningKeySlop, INTERVAL, "1 day"), + V(OptimisticData, AUTOBOOL, "auto"), V(PortForwarding, BOOL, "0"), V(PortForwardingHelper, FILENAME, "tor-fw-helper"), @@ -437,6 +457,7 @@ static config_var_t option_vars_[] = { V(V3BandwidthsFile, FILENAME, NULL), V(GuardfractionFile, FILENAME, NULL), VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"), + OBSOLETE("VoteOnHidServDirectoriesV2"), V(VirtualAddrNetworkIPv4, STRING, "127.192.0.0/10"), V(VirtualAddrNetworkIPv6, STRING, "[FE80::]/10"), V(WarnPlaintextPorts, CSV, "23,109,110,143"), @@ -449,7 +470,6 @@ static config_var_t option_vars_[] = { NULL), VAR("__OwningControllerProcess",STRING,OwningControllerProcess, NULL), V(MinUptimeHidServDirectoryV2, INTERVAL, "96 hours"), - V(VoteOnHidServDirectoriesV2, BOOL, "1"), V(TestingServerDownloadSchedule, CSV_INTERVAL, "0, 0, 0, 60, 60, 120, " "300, 900, 2147483647"), V(TestingClientDownloadSchedule, CSV_INTERVAL, "0, 0, 60, 300, 600, " @@ -460,16 +480,49 @@ static config_var_t option_vars_[] = { V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, " "300, 600, 1800, 3600, 3600, 3600, " "10800, 21600, 43200"), + /* With the ClientBootstrapConsensus*Download* below: + * Clients with only authorities will try: + * - 3 authorities over 10 seconds, then wait 60 minutes. + * Clients with authorities and fallbacks will try: + * - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes. + * Clients will also retry when an application request arrives. + * After a number of failed reqests, clients retry every 3 days + 1 hour. + * + * Clients used to try 2 authorities over 10 seconds, then wait for + * 60 minutes or an application request. + * + * When clients have authorities and fallbacks available, they use these + * schedules: (we stagger the times to avoid thundering herds) */ + V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, + "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), + V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, + "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), + /* When clients only have authorities available, they use this schedule: */ + V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, + "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"), + /* We don't want to overwhelm slow networks (or mirrors whose replies are + * blocked), but we also don't want to fail if only some mirrors are + * blackholed. Clients will try 3 directories simultaneously. + * (Relays never use simultaneous connections.) */ + V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), V(TestingConsensusMaxDownloadTries, UINT, "8"), + /* Since we try connections rapidly and simultaneously, we can afford + * to give up earlier. (This protects against overloading directories.) */ + V(ClientBootstrapConsensusMaxDownloadTries, UINT, "7"), + /* We want to give up much earlier if we're only using authorities. */ + V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"), V(TestingDescriptorMaxDownloadTries, UINT, "8"), V(TestingMicrodescMaxDownloadTries, UINT, "8"), V(TestingCertMaxDownloadTries, UINT, "8"), V(TestingDirAuthVoteExit, ROUTERSET, NULL), + V(TestingDirAuthVoteExitIsStrict, BOOL, "0"), V(TestingDirAuthVoteGuard, ROUTERSET, NULL), + V(TestingDirAuthVoteGuardIsStrict, BOOL, "0"), V(TestingDirAuthVoteHSDir, ROUTERSET, NULL), + V(TestingDirAuthVoteHSDirIsStrict, BOOL, "0"), VAR("___UsingTestNetworkDefaults", BOOL, UsingTestNetworkDefaults_, "0"), { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL } @@ -484,6 +537,14 @@ static const config_var_t testing_tor_network_defaults[] = { V(AssumeReachable, BOOL, "1"), V(AuthDirMaxServersPerAddr, UINT, "0"), V(AuthDirMaxServersPerAuthAddr,UINT, "0"), + V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, + "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), + V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, + "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), + V(ClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL, + "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"), + V(ClientBootstrapConsensusMaxDownloadTries, UINT, "80"), + V(ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"), V(ClientDNSRejectInternalAddresses, BOOL,"0"), V(ClientRejectInternalAddresses, BOOL, "0"), V(CountPrivateBandwidth, BOOL, "1"), @@ -531,7 +592,6 @@ static const config_var_t testing_tor_network_defaults[] = { static char *get_windows_conf_root(void); #endif static int options_act_reversible(const or_options_t *old_options, char **msg); -static int options_act(const or_options_t *old_options); static int options_transition_allowed(const or_options_t *old, const or_options_t *new, char **msg); @@ -542,16 +602,12 @@ static int options_transition_affects_descriptor( static int check_nickname_list(char **lst, const char *name, char **msg); static char *get_bindaddr_from_transport_listen_line(const char *line, const char *transport); -static int parse_dir_authority_line(const char *line, - dirinfo_type_t required_type, - int validate_only); -static int parse_dir_fallback_line(const char *line, - int validate_only); -static void port_cfg_free(port_cfg_t *port); static int parse_ports(or_options_t *options, int validate_only, - char **msg_out, int *n_ports_out); + char **msg_out, int *n_ports_out, + int *world_writable_control_socket); static int check_server_ports(const smartlist_t *ports, - const or_options_t *options); + const or_options_t *options, + int *num_low_ports_out); static int validate_data_directory(or_options_t *options); static int write_configuration_file(const char *fname, @@ -610,15 +666,15 @@ static char *global_dirfrontpagecontents = NULL; static smartlist_t *configured_ports = NULL; /** Return the contents of our frontpage string, or NULL if not configured. */ -const char * -get_dirportfrontpage(void) +MOCK_IMPL(const char*, +get_dirportfrontpage, (void)) { return global_dirfrontpagecontents; } -/** Return the currently configured options. */ -or_options_t * -get_options_mutable(void) +/** Returns the currently configured options. */ +MOCK_IMPL(or_options_t *, +get_options_mutable, (void)) { tor_assert(global_options); return global_options; @@ -749,6 +805,7 @@ or_options_free(or_options_t *options) } tor_free(options->BridgePassword_AuthDigest_); tor_free(options->command_arg); + tor_free(options->master_key_fname); config_free(&options_format, options); } @@ -777,7 +834,6 @@ config_free_all(void) tor_free(torrc_fname); tor_free(torrc_defaults_fname); - tor_free(the_tor_version); tor_free(global_dirfrontpagecontents); tor_free(the_short_tor_version); @@ -841,64 +897,76 @@ escaped_safe_str(const char *address) return escaped(address); } +/** List of default directory authorities */ + +static const char *default_authorities[] = { + "moria1 orport=9101 " + "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " + "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", + "tor26 orport=443 " + "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " + "ipv6=[2001:858:2:2:aabb:0:563b:1526]:443 " + "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", + "dizum orport=443 " + "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " + "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", + "Bifroest orport=443 bridge " + "37.218.247.217:80 1D8F 3A91 C37C 5D1C 4C19 B1AD 1D0C FBE8 BF72 D8E1", + "gabelmoo orport=443 " + "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " + "ipv6=[2001:638:a000:4140::ffff:189]:443 " + "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", + "dannenberg orport=443 " + "v3ident=0232AF901C31A04EE9848595AF9BB7620D4C5B2E " + "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", + "maatuska orport=80 " + "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " + "ipv6=[2001:67c:289c::9]:80 " + "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", + "Faravahar orport=443 " + "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " + "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", + "longclaw orport=443 " + "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 " + "ipv6=[2620:13:4000:8000:60:f3ff:fea1:7cff]:443 " + "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145", + NULL +}; + +/** List of fallback directory authorities. The list is generated by opt-in of + * relays that meet certain stability criteria. + */ +static const char *default_fallbacks[] = { +#include "fallback_dirs.inc" + NULL +}; + /** Add the default directory authorities directly into the trusted dir list, * but only add them insofar as they share bits with <b>type</b>. * Each authority's bits are restricted to the bits shared with <b>type</b>. * If <b>type</b> is ALL_DIRINFO or NO_DIRINFO (zero), add all authorities. */ -static void +STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type) { int i; - const char *authorities[] = { - "moria1 orport=9101 " - "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 " - "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31", - "tor26 orport=443 " - "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " - "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D", - "dizum orport=443 " - "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 " - "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755", - "Tonga orport=443 bridge " - "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D", - "gabelmoo orport=443 " - "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 " - "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281", - "dannenberg orport=443 " - "v3ident=0232AF901C31A04EE9848595AF9BB7620D4C5B2E " - "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123", - "maatuska orport=80 " - "v3ident=49015F787433103580E3B66A1707A00E60F2D15B " - "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810", - "Faravahar orport=443 " - "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 " - "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC", - "longclaw orport=443 " - "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 " - "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145", - NULL - }; - for (i=0; authorities[i]; i++) { - if (parse_dir_authority_line(authorities[i], type, 0)<0) { + for (i=0; default_authorities[i]; i++) { + if (parse_dir_authority_line(default_authorities[i], type, 0)<0) { log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s", - authorities[i]); + default_authorities[i]); } } } /** Add the default fallback directory servers into the fallback directory * server list. */ -static void -add_default_fallback_dir_servers(void) +MOCK_IMPL(void, +add_default_fallback_dir_servers,(void)) { int i; - const char *fallback[] = { - NULL - }; - for (i=0; fallback[i]; i++) { - if (parse_dir_fallback_line(fallback[i], 0)<0) { + for (i=0; default_fallbacks[i]; i++) { + if (parse_dir_fallback_line(default_fallbacks[i], 0)<0) { log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s", - fallback[i]); + default_fallbacks[i]); } } } @@ -958,7 +1026,7 @@ validate_dir_servers(or_options_t *options, or_options_t *old_options) /** Look at all the config options and assign new dir authorities * as appropriate. */ -static int +int consider_adding_dir_servers(const or_options_t *options, const or_options_t *old_options) { @@ -968,6 +1036,7 @@ consider_adding_dir_servers(const or_options_t *options, !smartlist_len(router_get_fallback_dir_servers()) || !old_options || !config_lines_eq(options->DirAuthorities, old_options->DirAuthorities) || !config_lines_eq(options->FallbackDir, old_options->FallbackDir) || + (options->UseDefaultFallbackDirs != old_options->UseDefaultFallbackDirs) || !config_lines_eq(options->AlternateBridgeAuthority, old_options->AlternateBridgeAuthority) || !config_lines_eq(options->AlternateDirAuthority, @@ -976,23 +1045,36 @@ consider_adding_dir_servers(const or_options_t *options, if (!need_to_update) return 0; /* all done */ + /* "You cannot set both DirAuthority and Alternate*Authority." + * Checking that this restriction holds allows us to simplify + * the unit tests. */ + tor_assert(!(options->DirAuthorities && + (options->AlternateDirAuthority + || options->AlternateBridgeAuthority))); + /* Start from a clean slate. */ clear_dir_servers(); if (!options->DirAuthorities) { /* then we may want some of the defaults */ dirinfo_type_t type = NO_DIRINFO; - if (!options->AlternateBridgeAuthority) + if (!options->AlternateBridgeAuthority) { type |= BRIDGE_DIRINFO; - if (!options->AlternateDirAuthority) + } + if (!options->AlternateDirAuthority) { type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO; + /* Only add the default fallback directories when the DirAuthorities, + * AlternateDirAuthority, and FallbackDir directory config options + * are set to their defaults, and when UseDefaultFallbackDirs is 1. */ + if (!options->FallbackDir && options->UseDefaultFallbackDirs) { + add_default_fallback_dir_servers(); + } + } /* if type == NO_DIRINFO, we don't want to add any of the * default authorities, because we've replaced them all */ if (type != NO_DIRINFO) add_default_trusted_dir_authorities(type); } - if (!options->FallbackDir) - add_default_fallback_dir_servers(); for (cl = options->DirAuthorities; cl; cl = cl->next) if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0) @@ -1009,6 +1091,9 @@ consider_adding_dir_servers(const or_options_t *options, return 0; } +/* Helps determine flags to pass to switch_id. */ +static int have_low_ports = -1; + /** Fetch the active option list, and take actions based on it. All of the * things we do should survive being done repeatedly. If present, * <b>old_options</b> contains the previous value of the options. @@ -1077,6 +1162,9 @@ options_act_reversible(const or_options_t *old_options, char **msg) init_libevent(options); libevent_initialized = 1; + /* This has to come up after libevent is initialized. */ + control_initialize_event_queue(); + /* * Initialize the scheduler - this has to come after * options_init_from_torrc() sets up libevent - why yes, that seems @@ -1087,7 +1175,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Adjust the port configuration so we can launch listeners. */ - if (parse_ports(options, 0, msg, &n_ports)) { + if (parse_ports(options, 0, msg, &n_ports, NULL)) { if (!*msg) *msg = tor_strdup("Unexpected problem parsing port config"); goto rollback; @@ -1140,7 +1228,16 @@ options_act_reversible(const or_options_t *old_options, char **msg) /* Setuid/setgid as appropriate */ if (options->User) { - if (switch_id(options->User) != 0) { + tor_assert(have_low_ports != -1); + unsigned switch_id_flags = 0; + if (options->KeepBindCapabilities == 1) { + switch_id_flags |= SWITCH_ID_KEEP_BINDLOW; + switch_id_flags |= SWITCH_ID_WARN_IF_NO_CAPS; + } + if (options->KeepBindCapabilities == -1 && have_low_ports) { + switch_id_flags |= SWITCH_ID_KEEP_BINDLOW; + } + if (switch_id(options->User, switch_id_flags) != 0) { /* No need to roll back, since you can't change the value. */ *msg = tor_strdup("Problem with User value. See logs for details."); goto done; @@ -1148,16 +1245,30 @@ options_act_reversible(const or_options_t *old_options, char **msg) } /* Ensure data directory is private; create if possible. */ + cpd_check_t cpd_opts = running_tor ? CPD_CREATE : CPD_CHECK; + if (options->DataDirectoryGroupReadable) + cpd_opts |= CPD_GROUP_READ; if (check_private_dir(options->DataDirectory, - running_tor ? CPD_CREATE : CPD_CHECK, + cpd_opts, options->User)<0) { tor_asprintf(msg, "Couldn't access/create private data directory \"%s\"", options->DataDirectory); + goto done; /* No need to roll back, since you can't change the value. */ } +#ifndef _WIN32 + if (options->DataDirectoryGroupReadable) { + /* Only new dirs created get new opts, also enforce group read. */ + if (chmod(options->DataDirectory, 0750)) { + log_warn(LD_FS,"Unable to make %s group-readable: %s", + options->DataDirectory, strerror(errno)); + } + } +#endif + /* Bail out at this point if we're not going to be a client or server: * we don't run Tor itself. */ if (!running_tor) @@ -1315,10 +1426,6 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options, if (!old_options) return 0; - if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) { - return 1; - } - if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup)) return 1; @@ -1334,7 +1441,7 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options, * Note: We haven't moved all the "act on new configuration" logic * here yet. Some is still in do_hup() and other places. */ -static int +STATIC int options_act(const or_options_t *old_options) { config_line_t *cl; @@ -1356,10 +1463,12 @@ options_act(const or_options_t *old_options) if (options->DisableDebuggerAttachment && !disabled_debugger_attach && running_tor) { int ok = tor_disable_debugger_attach(); + /* LCOV_EXCL_START the warned_debugger_attach is 0 can't reach inside. */ if (warned_debugger_attach && ok == 1) { log_notice(LD_CONFIG, "Disabled attaching debuggers for unprivileged " "users."); } + /* LCOV_EXCL_STOP */ disabled_debugger_attach = (ok == 1); } else if (!options->DisableDebuggerAttachment && !warned_debugger_attach) { @@ -1386,12 +1495,14 @@ options_act(const or_options_t *old_options) #endif #ifdef ENABLE_TOR2WEB_MODE +/* LCOV_EXCL_START */ if (!options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was compiled to run in " "'tor2web mode'. It can only be run with the Tor2webMode torrc " "option enabled."); return -1; } +/* LCOV_EXCL_STOP */ #else if (options->Tor2webMode) { log_err(LD_CONFIG, "This copy of Tor was not compiled to run in " @@ -1403,7 +1514,7 @@ options_act(const or_options_t *old_options) #endif /* If we are a bridge with a pluggable transport proxy but no - Extended ORPort, inform the user that she is missing out. */ + Extended ORPort, inform the user that they are missing out. */ if (server_mode(options) && options->ServerTransportPlugin && !options->ExtORPort_lines) { log_notice(LD_CONFIG, "We use pluggable transports but the Extended " @@ -1500,24 +1611,6 @@ options_act(const or_options_t *old_options) finish_daemon(options->DataDirectory); } - /* If needed, generate a new TLS DH prime according to the current torrc. */ - if (server_mode(options) && options->DynamicDHGroups) { - char *keydir = get_datadir_fname("keys"); - if (check_private_dir(keydir, CPD_CREATE, options->User)) { - tor_free(keydir); - return -1; - } - tor_free(keydir); - - if (!old_options || !old_options->DynamicDHGroups) { - char *fname = get_datadir_fname2("keys", "dynamic_dh_params"); - crypto_set_tls_dh_prime(fname); - tor_free(fname); - } - } else { /* clients don't need a dynamic DH prime. */ - crypto_set_tls_dh_prime(NULL); - } - /* We want to reinit keys as needed before we do much of anything else: keys are important, and other things can depend on them. */ if (transition_affects_workers || @@ -1673,8 +1766,8 @@ options_act(const or_options_t *old_options) if (revise_trackexithosts) addressmap_clear_excluded_trackexithosts(options); - if (!options->AutomapHostsOnResolve) { - if (old_options->AutomapHostsOnResolve) + if (!options->AutomapHostsOnResolve && + old_options->AutomapHostsOnResolve) { revise_automap_entries = 1; } else { if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes, @@ -1755,6 +1848,7 @@ options_act(const or_options_t *old_options) if (!public_server_mode(options)) { options->CellStatistics = 0; options->EntryStatistics = 0; + options->ConnDirectionStatistics = 0; options->HiddenServiceStatistics = 0; options->ExitPortStatistics = 0; } @@ -1812,8 +1906,8 @@ options_act(const or_options_t *old_options) print_notice = 1; } if (print_notice) - log_notice(LD_CONFIG, "Configured to measure statistics. Look for " - "the *-stats files that will first be written to the " + log_notice(LD_CONFIG, "Configured to measure statistics. Look for " + "the *-stats files that will first be written to the " "data directory in 24 hours from now."); } @@ -1887,28 +1981,42 @@ options_act(const or_options_t *old_options) return 0; } +typedef enum { + TAKES_NO_ARGUMENT = 0, + ARGUMENT_NECESSARY = 1, + ARGUMENT_OPTIONAL = 2 +} takes_argument_t; + static const struct { const char *name; - int takes_argument; + takes_argument_t takes_argument; } CMDLINE_ONLY_OPTIONS[] = { - { "-f", 1 }, - { "--allow-missing-torrc", 0 }, - { "--defaults-torrc", 1 }, - { "--hash-password", 1 }, - { "--dump-config", 1 }, - { "--list-fingerprint", 0 }, - { "--verify-config", 0 }, - { "--ignore-missing-torrc", 0 }, - { "--quiet", 0 }, - { "--hush", 0 }, - { "--version", 0 }, - { "--library-versions", 0 }, - { "-h", 0 }, - { "--help", 0 }, - { "--list-torrc-options", 0 }, - { "--digests", 0 }, - { "--nt-service", 0 }, - { "-nt-service", 0 }, + { "-f", ARGUMENT_NECESSARY }, + { "--allow-missing-torrc", TAKES_NO_ARGUMENT }, + { "--defaults-torrc", ARGUMENT_NECESSARY }, + { "--hash-password", ARGUMENT_NECESSARY }, + { "--dump-config", ARGUMENT_OPTIONAL }, + { "--list-fingerprint", TAKES_NO_ARGUMENT }, + { "--keygen", TAKES_NO_ARGUMENT }, + { "--newpass", TAKES_NO_ARGUMENT }, +#if 0 +/* XXXX028: This is not working yet in 0.2.7, so disabling with the + * minimal code modification. */ + { "--master-key", ARGUMENT_NECESSARY }, +#endif + { "--no-passphrase", TAKES_NO_ARGUMENT }, + { "--passphrase-fd", ARGUMENT_NECESSARY }, + { "--verify-config", TAKES_NO_ARGUMENT }, + { "--ignore-missing-torrc", TAKES_NO_ARGUMENT }, + { "--quiet", TAKES_NO_ARGUMENT }, + { "--hush", TAKES_NO_ARGUMENT }, + { "--version", TAKES_NO_ARGUMENT }, + { "--library-versions", TAKES_NO_ARGUMENT }, + { "-h", TAKES_NO_ARGUMENT }, + { "--help", TAKES_NO_ARGUMENT }, + { "--list-torrc-options", TAKES_NO_ARGUMENT }, + { "--nt-service", TAKES_NO_ARGUMENT }, + { "-nt-service", TAKES_NO_ARGUMENT }, { NULL, 0 }, }; @@ -1935,7 +2043,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, while (i < argc) { unsigned command = CONFIG_LINE_NORMAL; - int want_arg = 1; + takes_argument_t want_arg = ARGUMENT_NECESSARY; int is_cmdline = 0; int j; @@ -1965,7 +2073,9 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, want_arg = 0; } - if (want_arg && i == argc-1) { + const int is_last = (i == argc-1); + + if (want_arg == ARGUMENT_NECESSARY && is_last) { if (ignore_errors) { arg = strdup(""); } else { @@ -1975,8 +2085,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors, config_free_lines(front_cmdline); return -1; } + } else if (want_arg == ARGUMENT_OPTIONAL && is_last) { + arg = tor_strdup(""); } else { - arg = want_arg ? tor_strdup(argv[i+1]) : strdup(""); + arg = (want_arg != TAKES_NO_ARGUMENT) ? tor_strdup(argv[i+1]) : + tor_strdup(""); } param = tor_malloc_zero(sizeof(config_line_t)); @@ -2077,7 +2190,7 @@ print_usage(void) printf( "Copyright (c) 2001-2004, Roger Dingledine\n" "Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson\n" -"Copyright (c) 2007-2015, The Tor Project, Inc.\n\n" +"Copyright (c) 2007-2016, The Tor Project, Inc.\n\n" "tor -f <torrc> [args]\n" "See man page for options, or https://www.torproject.org/ for " "documentation.\n"); @@ -2563,6 +2676,66 @@ options_validate_cb(void *old_options, void *options, void *default_options, from_setconf, msg); } +#define REJECT(arg) \ + STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END +#ifdef __GNUC__ +#define COMPLAIN(args...) \ + STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END +#else +#define COMPLAIN(args, ...) \ + STMT_BEGIN log_warn(LD_CONFIG, args, ##__VA_ARGS__); STMT_END +#endif + +/** Log a warning message iff <b>filepath</b> is not absolute. + * Warning message must contain option name <b>option</b> and + * an absolute path that <b>filepath</b> will resolve to. + * + * In case <b>filepath</b> is absolute, do nothing. + */ +static void +warn_if_option_path_is_relative(const char *option, + char *filepath) +{ + if (filepath && path_is_relative(filepath)) { + char *abs_path = make_path_absolute(filepath); + COMPLAIN("Path for %s (%s) is relative and will resolve to %s." + " Is this what you wanted?", option, filepath, abs_path); + tor_free(abs_path); + } +} + +/** Scan <b>options</b> for occurances of relative file/directory + * path and log a warning whenever it is found. + */ +static void +warn_about_relative_paths(or_options_t *options) +{ + tor_assert(options); + + warn_if_option_path_is_relative("CookieAuthFile", + options->CookieAuthFile); + warn_if_option_path_is_relative("ExtORPortCookieAuthFile", + options->ExtORPortCookieAuthFile); + warn_if_option_path_is_relative("DirPortFrontPage", + options->DirPortFrontPage); + warn_if_option_path_is_relative("V3BandwidthsFile", + options->V3BandwidthsFile); + warn_if_option_path_is_relative("ControlPortWriteToFile", + options->ControlPortWriteToFile); + warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile); + warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File); + warn_if_option_path_is_relative("Log",options->DebugLogFile); + warn_if_option_path_is_relative("AccelDir",options->AccelDir); + warn_if_option_path_is_relative("DataDirectory",options->DataDirectory); + warn_if_option_path_is_relative("PidFile",options->PidFile); + + for (config_line_t *hs_line = options->RendConfigLines; hs_line; + hs_line = hs_line->next) { + if (!strcasecmp(hs_line->key, "HiddenServiceDir")) + warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value); + } +} + /** Return 0 if every setting in <b>options</b> is reasonable, is a * permissible transition from <b>old_options</b>, and none of the * testing-only settings differ from <b>default_options</b> unless in @@ -2584,13 +2757,13 @@ options_validate(or_options_t *old_options, or_options_t *options, config_line_t *cl; const char *uname = get_uname(); int n_ports=0; -#define REJECT(arg) \ - STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END -#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END + int world_writable_control_socket=0; tor_assert(msg); *msg = NULL; + warn_about_relative_paths(options); + if (server_mode(options) && (!strcmpstart(uname, "Windows 95") || !strcmpstart(uname, "Windows 98") || @@ -2601,7 +2774,8 @@ options_validate(or_options_t *old_options, or_options_t *options, "for details.", uname); } - if (parse_ports(options, 1, msg, &n_ports) < 0) + if (parse_ports(options, 1, msg, &n_ports, + &world_writable_control_socket) < 0) return -1; if (parse_outbound_addresses(options, 1, msg) < 0) @@ -2682,7 +2856,7 @@ options_validate(or_options_t *old_options, or_options_t *options, options->TransProxyType_parsed = TPT_TPROXY; #endif } else if (!strcasecmp(options->TransProxyType, "ipfw")) { -#if !defined(__FreeBSD__) && !defined( DARWIN ) +#ifndef KERNEL_MAY_SUPPORT_IPFW /* Earlier versions of OS X have ipfw */ REJECT("ipfw is a FreeBSD-specific" "and OS X/Darwin-specific feature."); @@ -2744,6 +2918,9 @@ options_validate(or_options_t *old_options, or_options_t *options, COMPLAIN("Unrecognized TLSECGroup: Falling back to the default."); tor_free(options->TLSECGroup); } + if (!evaluate_ecgroup_for_tls(options->TLSECGroup)) { + REJECT("Unsupported TLSECGroup."); + } if (options->ExcludeNodes && options->StrictNodes) { COMPLAIN("You have asked to exclude certain relays from all positions " @@ -2898,6 +3075,8 @@ options_validate(or_options_t *old_options, or_options_t *options, } } + /* Terminate Reachable*Addresses with reject * + */ for (i=0; i<3; i++) { config_line_t **linep = (i==0) ? &options->ReachableAddresses : @@ -2907,8 +3086,6 @@ options_validate(or_options_t *old_options, or_options_t *options, continue; /* We need to end with a reject *:*, not an implicit accept *:* */ for (;;) { - if (!strcmp((*linep)->value, "reject *:*")) /* already there */ - break; linep = &((*linep)->next); if (!*linep) { *linep = tor_malloc_zero(sizeof(config_line_t)); @@ -2924,11 +3101,29 @@ options_validate(or_options_t *old_options, or_options_t *options, if ((options->ReachableAddresses || options->ReachableORAddresses || - options->ReachableDirAddresses) && + options->ReachableDirAddresses || + options->ClientUseIPv4 == 0) && server_mode(options)) REJECT("Servers must be able to freely connect to the rest " "of the Internet, so they must not set Reachable*Addresses " - "or FascistFirewall."); + "or FascistFirewall or FirewallPorts or ClientUseIPv4 0."); + + /* We check if Reachable*Addresses blocks all addresses in + * parse_reachable_addresses(). */ + +#define WARN_PLEASE_USE_IPV6_LOG_MSG \ + "ClientPreferIPv6%sPort 1 is ignored unless tor is using IPv6. " \ + "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges." + + if (!fascist_firewall_use_ipv6(options) + && options->ClientPreferIPv6ORPort == 1) + log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "OR"); + + if (!fascist_firewall_use_ipv6(options) + && options->ClientPreferIPv6DirPort == 1) + log_warn(LD_CONFIG, WARN_PLEASE_USE_IPV6_LOG_MSG, "Dir"); + +#undef WARN_PLEASE_USE_IPV6_LOG_MSG if (options->UseBridges && server_mode(options)) @@ -3082,6 +3277,21 @@ options_validate(or_options_t *old_options, or_options_t *options, "http://freehaven.net/anonbib/#hs-attack06 for details."); } + if (options->EntryNodes && + routerset_is_list(options->EntryNodes) && + (routerset_len(options->EntryNodes) == 1) && + (options->RendConfigLines != NULL)) { + tor_asprintf(msg, + "You have one single EntryNodes and at least one hidden service " + "configured. This is bad because it's very easy to locate your " + "entry guard which can then lead to the deanonymization of your " + "hidden service -- for more details, see " + "https://trac.torproject.org/projects/tor/ticket/14917. " + "For this reason, the use of one EntryNodes with an hidden " + "service is prohibited until a better solution is found."); + return -1; + } + if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { log_warn(LD_CONFIG, @@ -3265,8 +3475,30 @@ options_validate(or_options_t *old_options, or_options_t *options, options->AccountingRule = ACCT_SUM; else if (!strcmp(options->AccountingRule_option, "max")) options->AccountingRule = ACCT_MAX; + else if (!strcmp(options->AccountingRule_option, "in")) + options->AccountingRule = ACCT_IN; + else if (!strcmp(options->AccountingRule_option, "out")) + options->AccountingRule = ACCT_OUT; else - REJECT("AccountingRule must be 'sum' or 'max'"); + REJECT("AccountingRule must be 'sum', 'max', 'in', or 'out'"); + } + + if (options->DirPort_set && !options->DirCache) { + REJECT("DirPort configured but DirCache disabled. DirPort requires " + "DirCache."); + } + + if (options->BridgeRelay && !options->DirCache) { + REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires " + "DirCache."); + } + + if (server_mode(options)) { + char *msg = NULL; + if (have_enough_mem_for_dircache(options, 0, &msg)) { + log_warn(LD_CONFIG, "%s", msg); + tor_free(msg); + } } if (options->HTTPProxy) { /* parse it now */ @@ -3379,13 +3611,16 @@ options_validate(or_options_t *old_options, or_options_t *options, } } - if (options->ControlPort_set && !options->HashedControlPassword && + if ((options->ControlPort_set || world_writable_control_socket) && + !options->HashedControlPassword && !options->HashedControlSessionPassword && !options->CookieAuthentication) { - log_warn(LD_CONFIG, "ControlPort is open, but no authentication method " + log_warn(LD_CONFIG, "Control%s is %s, but no authentication method " "has been configured. This means that any program on your " "computer can reconfigure your Tor. That's bad! You should " - "upgrade your Tor controller as soon as possible."); + "upgrade your Tor controller as soon as possible.", + options->ControlPort_set ? "Port" : "Socket", + options->ControlPort_set ? "open" : "world writable"); } if (options->CookieAuthFileGroupReadable && !options->CookieAuthFile) { @@ -3414,6 +3649,13 @@ options_validate(or_options_t *old_options, or_options_t *options, if (validate_addr_policies(options, msg) < 0) return -1; + /* If FallbackDir is set, we don't UseDefaultFallbackDirs */ + if (options->UseDefaultFallbackDirs && options->FallbackDir) { + log_info(LD_CONFIG, "You have set UseDefaultFallbackDirs 1 and " + "FallbackDir(s). Ignoring UseDefaultFallbackDirs, and " + "using the FallbackDir(s) you have set."); + } + if (validate_dir_servers(options, old_options) < 0) REJECT("Directory authority/fallback line did not parse. See logs " "for details."); @@ -3623,8 +3865,20 @@ options_validate(or_options_t *old_options, or_options_t *options, CHECK_DEFAULT(TestingDescriptorMaxDownloadTries); CHECK_DEFAULT(TestingMicrodescMaxDownloadTries); CHECK_DEFAULT(TestingCertMaxDownloadTries); + CHECK_DEFAULT(TestingAuthKeyLifetime); + CHECK_DEFAULT(TestingLinkCertLifetime); + CHECK_DEFAULT(TestingSigningKeySlop); + CHECK_DEFAULT(TestingAuthKeySlop); + CHECK_DEFAULT(TestingLinkKeySlop); #undef CHECK_DEFAULT + if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2) + REJECT("SigningKeyLifetime is too short."); + if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2) + REJECT("LinkCertLifetime is too short."); + if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) + REJECT("TestingAuthKeyLifetime is too short."); + if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL_TESTING_INITIAL) { REJECT("TestingV3AuthInitialVotingInterval is insanely low."); @@ -3682,11 +3936,41 @@ options_validate(or_options_t *old_options, or_options_t *options, } if (options->TestingConsensusMaxDownloadTries < 2) { - REJECT("TestingConsensusMaxDownloadTries must be greater than 1."); + REJECT("TestingConsensusMaxDownloadTries must be greater than 2."); } else if (options->TestingConsensusMaxDownloadTries > 800) { COMPLAIN("TestingConsensusMaxDownloadTries is insanely high."); } + if (options->ClientBootstrapConsensusMaxDownloadTries < 2) { + REJECT("ClientBootstrapConsensusMaxDownloadTries must be greater " + "than 2." + ); + } else if (options->ClientBootstrapConsensusMaxDownloadTries > 800) { + COMPLAIN("ClientBootstrapConsensusMaxDownloadTries is insanely " + "high."); + } + + if (options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries + < 2) { + REJECT("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must " + "be greater than 2." + ); + } else if ( + options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries + > 800) { + COMPLAIN("ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is " + "insanely high."); + } + + if (options->ClientBootstrapConsensusMaxInProgressTries < 1) { + REJECT("ClientBootstrapConsensusMaxInProgressTries must be greater " + "than 0."); + } else if (options->ClientBootstrapConsensusMaxInProgressTries + > 100) { + COMPLAIN("ClientBootstrapConsensusMaxInProgressTries is insanely " + "high."); + } + if (options->TestingDescriptorMaxDownloadTries < 2) { REJECT("TestingDescriptorMaxDownloadTries must be greater than 1."); } else if (options->TestingDescriptorMaxDownloadTries > 800) { @@ -3752,9 +4036,10 @@ options_validate(or_options_t *old_options, or_options_t *options, "combination."); return 0; +} + #undef REJECT #undef COMPLAIN -} /* Given the value that the user has set for MaxMemInQueues, compute the * actual maximum value. We clip this value if it's too low, and autodetect @@ -3818,6 +4103,52 @@ compute_real_max_mem_in_queues(const uint64_t val, int log_guess) } } +/* If we have less than 300 MB suggest disabling dircache */ +#define DIRCACHE_MIN_MB_BANDWIDTH 300 +#define DIRCACHE_MIN_BANDWIDTH (DIRCACHE_MIN_MB_BANDWIDTH*ONE_MEGABYTE) +#define STRINGIFY(val) #val + +/** Create a warning message for emitting if we are a dircache but may not have + * enough system memory, or if we are not a dircache but probably should be. + * Return -1 when a message is returned in *msg*, else return 0. */ +STATIC int +have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem, + char **msg) +{ + *msg = NULL; + /* XXX We should possibly be looking at MaxMemInQueues here + * unconditionally. Or we should believe total_mem unconditionally. */ + if (total_mem == 0) { + if (get_total_system_memory(&total_mem) < 0) { + total_mem = options->MaxMemInQueues >= SIZE_MAX ? + SIZE_MAX : (size_t)options->MaxMemInQueues; + } + } + if (options->DirCache) { + if (total_mem < DIRCACHE_MIN_BANDWIDTH) { + if (options->BridgeRelay) { + *msg = strdup("Running a Bridge with less than " + STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is " + "not recommended."); + } else { + *msg = strdup("Being a directory cache (default) with less than " + STRINGIFY(DIRCACHE_MIN_MB_BANDWIDTH) " MB of memory is " + "not recommended and may consume most of the available " + "resources, consider disabling this functionality by " + "setting the DirCache option to 0."); + } + } + } else { + if (total_mem >= DIRCACHE_MIN_BANDWIDTH) { + *msg = strdup("DirCache is disabled and we are configured as a " + "relay. This may disqualify us from becoming a guard in the " + "future."); + } + } + return *msg == NULL ? 0 : -1; +} +#undef STRINGIFY + /** Helper: return true iff s1 and s2 are both NULL, or both non-NULL * equal strings. */ static int @@ -3865,6 +4196,18 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->KeepBindCapabilities != new_val->KeepBindCapabilities) { + *msg = tor_strdup("While Tor is running, changing KeepBindCapabilities is " + "not allowed."); + return -1; + } + + if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) { + *msg = tor_strdup("While Tor is running, changing " + "SyslogIdentityTag is not allowed."); + return -1; + } + if ((old->HardwareAccel != new_val->HardwareAccel) || !opt_streq(old->AccelName, new_val->AccelName) || !opt_streq(old->AccelDir, new_val->AccelDir)) { @@ -3913,6 +4256,7 @@ options_transition_allowed(const or_options_t *old, } \ } while (0) + SB_NOCHANGE_STR(Address); SB_NOCHANGE_STR(PidFile); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); @@ -3994,7 +4338,10 @@ options_transition_affects_descriptor(const or_options_t *old_options, !opt_streq(old_options->MyFamily, new_options->MyFamily) || !opt_streq(old_options->AccountingStart, new_options->AccountingStart) || old_options->AccountingMax != new_options->AccountingMax || - public_server_mode(old_options) != public_server_mode(new_options)) + old_options->AccountingRule != new_options->AccountingRule || + public_server_mode(old_options) != public_server_mode(new_options) || + old_options->DirCache != new_options->DirCache || + old_options->AssumeReachable != new_options->AssumeReachable) return 1; return 0; @@ -4330,13 +4677,6 @@ options_init_from_torrc(int argc, char **argv) exit(0); } - if (config_line_find(cmdline_only_options, "--digests")) { - printf("Tor version %s.\n",get_version()); - printf("%s", libor_get_digests()); - printf("%s", tor_get_digests()); - exit(0); - } - if (config_line_find(cmdline_only_options, "--library-versions")) { printf("Tor version %s. \n", get_version()); printf("Library versions\tCompiled\t\tRuntime\n"); @@ -4355,7 +4695,9 @@ options_init_from_torrc(int argc, char **argv) command = CMD_RUN_TOR; for (p_index = cmdline_only_options; p_index; p_index = p_index->next) { - if (!strcmp(p_index->key,"--list-fingerprint")) { + if (!strcmp(p_index->key,"--keygen")) { + command = CMD_KEYGEN; + } else if (!strcmp(p_index->key,"--list-fingerprint")) { command = CMD_LIST_FINGERPRINT; } else if (!strcmp(p_index->key, "--hash-password")) { command = CMD_HASH_PASSWORD; @@ -4398,6 +4740,65 @@ options_init_from_torrc(int argc, char **argv) retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg); + if (retval < 0) + goto err; + + if (config_line_find(cmdline_only_options, "--no-passphrase")) { + if (command == CMD_KEYGEN) { + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; + } else { + log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); + exit(1); + } + } + + if (config_line_find(cmdline_only_options, "--newpass")) { + if (command == CMD_KEYGEN) { + get_options_mutable()->change_key_passphrase = 1; + } else { + log_err(LD_CONFIG, "--newpass specified without --keygen!"); + exit(1); + } + } + + { + const config_line_t *fd_line = config_line_find(cmdline_only_options, + "--passphrase-fd"); + if (fd_line) { + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); + exit(1); + } else if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); + exit(1); + } else { + const char *v = fd_line->value; + int ok = 1; + long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); + if (fd < 0 || ok == 0) { + log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); + exit(1); + } + get_options_mutable()->keygen_passphrase_fd = (int)fd; + get_options_mutable()->use_keygen_passphrase_fd = 1; + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_ON; + } + } + } + + { + const config_line_t *key_line = config_line_find(cmdline_only_options, + "--master-key"); + if (key_line) { + if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--master-key without --keygen!"); + exit(1); + } else { + get_options_mutable()->master_key_fname = tor_strdup(key_line->value); + } + } + } + err: tor_free(cf); @@ -4752,7 +5153,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, !strcasecmp(smartlist_get(elts,0), "syslog")) { #ifdef HAVE_SYSLOG_H if (!validate_only) { - add_syslog_log(severity); + add_syslog_log(severity, options->SyslogIdentityTag); } #else log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry."); @@ -5325,13 +5726,14 @@ get_options_for_server_transport(const char *transport) * (minus whatever bits it's missing) as a valid authority. * Return 0 on success or filtering out by type, * or -1 if the line isn't well-formed or if we can't add it. */ -static int +STATIC int parse_dir_authority_line(const char *line, dirinfo_type_t required_type, int validate_only) { smartlist_t *items = NULL; int r; char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL; + tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL; uint16_t dir_port = 0, or_port = 0; char digest[DIGEST_LEN]; char v3_digest[DIGEST_LEN]; @@ -5388,6 +5790,20 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type, } else { type |= V3_DIRINFO|EXTRAINFO_DIRINFO|MICRODESC_DIRINFO; } + } else if (!strcasecmpstart(flag, "ipv6=")) { + if (ipv6_addrport_ptr) { + log_warn(LD_CONFIG, "Redundant ipv6 addr/port on DirAuthority line"); + } else { + if (tor_addr_port_parse(LOG_WARN, flag+strlen("ipv6="), + &ipv6_addrport.addr, &ipv6_addrport.port, + -1) < 0 + || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) { + log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on DirAuthority line", + escaped(flag)); + goto err; + } + ipv6_addrport_ptr = &ipv6_addrport; + } } else { log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirAuthority line", flag); @@ -5430,6 +5846,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type, log_debug(LD_DIR, "Trusted %d dirserver at %s:%d (%s)", (int)type, address, (int)dir_port, (char*)smartlist_get(items,0)); if (!(ds = trusted_dir_server_new(nickname, address, dir_port, or_port, + ipv6_addrport_ptr, digest, v3_digest, type, weight))) goto err; dir_server_add(ds); @@ -5455,7 +5872,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type, * <b>validate_only</b> is 0, and the line is well-formed, then add the * dirserver described in the line as a fallback directory. Return 0 on * success, or -1 if the line isn't well-formed or if we can't add it. */ -static int +int parse_dir_fallback_line(const char *line, int validate_only) { @@ -5467,6 +5884,7 @@ parse_dir_fallback_line(const char *line, int ok; char id[DIGEST_LEN]; char *address=NULL; + tor_addr_port_t ipv6_addrport, *ipv6_addrport_ptr = NULL; double weight=1.0; memset(id, 0, sizeof(id)); @@ -5485,6 +5903,20 @@ parse_dir_fallback_line(const char *line, } else if (!strcmpstart(cp, "id=")) { ok = !base16_decode(id, DIGEST_LEN, cp+strlen("id="), strlen(cp)-strlen("id=")); + } else if (!strcasecmpstart(cp, "ipv6=")) { + if (ipv6_addrport_ptr) { + log_warn(LD_CONFIG, "Redundant ipv6 addr/port on FallbackDir line"); + } else { + if (tor_addr_port_parse(LOG_WARN, cp+strlen("ipv6="), + &ipv6_addrport.addr, &ipv6_addrport.port, + -1) < 0 + || tor_addr_family(&ipv6_addrport.addr) != AF_INET6) { + log_warn(LD_CONFIG, "Bad ipv6 addr/port %s on FallbackDir line", + escaped(cp)); + goto end; + } + ipv6_addrport_ptr = &ipv6_addrport; + } } else if (!strcmpstart(cp, "weight=")) { int ok; const char *wstring = cp + strlen("weight="); @@ -5526,7 +5958,8 @@ parse_dir_fallback_line(const char *line, if (!validate_only) { dir_server_t *ds; - ds = fallback_dir_server_new(&addr, dirport, orport, id, weight); + ds = fallback_dir_server_new(&addr, dirport, orport, ipv6_addrport_ptr, + id, weight); if (!ds) { log_warn(LD_CONFIG, "Couldn't create FallbackDir %s", escaped(line)); goto end; @@ -5545,7 +5978,7 @@ parse_dir_fallback_line(const char *line, } /** Allocate and return a new port_cfg_t with reasonable defaults. */ -static port_cfg_t * +STATIC port_cfg_t * port_cfg_new(size_t namelen) { tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1); @@ -5557,7 +5990,7 @@ port_cfg_new(size_t namelen) } /** Free all storage held in <b>port</b> */ -static void +STATIC void port_cfg_free(port_cfg_t *port) { tor_free(port); @@ -5611,12 +6044,12 @@ warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) } SMARTLIST_FOREACH_END(port); } -/** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there - * is listening on any non-loopback address. If <b>forbid</b> is true, - * then emit a stronger warning and remove the port from the list. +/** Given a list of port_cfg_t in <b>ports</b>, warn if any controller port + * there is listening on any non-loopback address. If <b>forbid_nonlocal</b> + * is true, then emit a stronger warning and remove the port from the list. */ static void -warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) +warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal) { int warned = 0; SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { @@ -5625,7 +6058,7 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) if (port->is_unix_addr) continue; if (!tor_addr_is_loopback(&port->addr)) { - if (forbid) { + if (forbid_nonlocal) { if (!warned) log_warn(LD_CONFIG, "You have a ControlPort set to accept " @@ -5653,14 +6086,6 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) } SMARTLIST_FOREACH_END(port); } -#define CL_PORT_NO_OPTIONS (1u<<0) -#define CL_PORT_WARN_NONLOCAL (1u<<1) -#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) -#define CL_PORT_SERVER_OPTIONS (1u<<3) -#define CL_PORT_FORBID_NONLOCAL (1u<<4) -#define CL_PORT_TAKES_HOSTNAMES (1u<<5) -#define CL_PORT_IS_UNIXSOCKET (1u<<6) - #ifdef HAVE_SYS_UN_H /** Parse the given <b>addrport</b> and set <b>path_out</b> if a Unix socket @@ -5727,12 +6152,12 @@ config_parse_unix_port(const char *addrport, char **path_out) * If no address is specified, default to <b>defaultaddr</b>. If no * FooPort is given, default to defaultport (if 0, there is no default). * - * If CL_PORT_NO_OPTIONS is set in <b>flags</b>, do not allow stream + * If CL_PORT_NO_STREAM_OPTIONS is set in <b>flags</b>, do not allow stream * isolation options in the FooPort entries. * * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the * ports are not on a local address. If CL_PORT_FORBID_NONLOCAL is set, - * this is a contrl port with no password set: don't even allow it. + * this is a control port with no password set: don't even allow it. * * Unless CL_PORT_ALLOW_EXTRA_LISTENADDR is set in <b>flags</b>, warn * if FooListenAddress is set but FooPort is 0. @@ -5748,7 +6173,7 @@ config_parse_unix_port(const char *addrport, char **path_out) * <b>out</b> for every port that the client should listen on. Return 0 * on success, -1 on failure. */ -static int +STATIC int parse_port_config(smartlist_t *out, const config_line_t *ports, const config_line_t *listenaddrs, @@ -5762,10 +6187,12 @@ parse_port_config(smartlist_t *out, int retval = -1; const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER); - const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS; + const unsigned allow_no_stream_options = flags & CL_PORT_NO_STREAM_OPTIONS; const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; + const unsigned default_to_group_writable = + flags & CL_PORT_DFLT_GROUP_WRITABLE; const unsigned allow_spurious_listenaddr = flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES; @@ -5880,6 +6307,7 @@ parse_port_config(smartlist_t *out, int sessiongroup = SESSION_GROUP_UNSET; unsigned isolation = ISO_DEFAULT; int prefer_no_auth = 0; + int socks_iso_keep_alive = 0; char *addrport; uint16_t ptmp=0; @@ -5889,7 +6317,9 @@ parse_port_config(smartlist_t *out, ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, cache_ipv4 = 1, use_cached_ipv4 = 0, cache_ipv6 = 0, use_cached_ipv6 = 0, - prefer_ipv6_automap = 1; + prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0, + relax_dirmode_check = 0, + has_used_unix_socket_only_option = 0; smartlist_split_string(elts, ports->value, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); @@ -5898,11 +6328,6 @@ parse_port_config(smartlist_t *out, goto err; } - if (allow_no_options && smartlist_len(elts) > 1) { - log_warn(LD_CONFIG, "Too many options on %sPort line", portname); - goto err; - } - /* Now parse the addr/port value */ addrport = smartlist_get(elts, 0); @@ -5931,7 +6356,8 @@ parse_port_config(smartlist_t *out, port = 1; } else if (!strcmp(addrport, "auto")) { port = CFG_AUTO_PORT; - tor_addr_parse(&addr, defaultaddr); + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); port = CFG_AUTO_PORT; @@ -5941,12 +6367,14 @@ parse_port_config(smartlist_t *out, tor_free(addrtmp); goto err; } + tor_free(addrtmp); } else { /* Try parsing integer port before address, because, who knows? "9050" might be a valid address. */ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); if (ok) { - tor_addr_parse(&addr, defaultaddr); + int af = tor_addr_parse(&addr, defaultaddr); + tor_assert(af >= 0); } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { if (ptmp == 0) { log_warn(LD_CONFIG, "%sPort line has address but no port", portname); @@ -5954,12 +6382,15 @@ parse_port_config(smartlist_t *out, } port = ptmp; } else { - log_warn(LD_CONFIG, "Couldn't parse address '%s' for %sPort", + log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort", escaped(addrport), portname); goto err; } } + if (unix_socket_path && default_to_group_writable) + group_writable = 1; + /* Now parse the rest of the options, if any. */ if (use_server_options) { /* This is a server port; parse advertising options */ @@ -6016,10 +6447,11 @@ parse_port_config(smartlist_t *out, const char *elt_orig = elt; if (elt_sl_idx == 0) continue; /* Skip addr:port */ + if (!strcasecmpstart(elt, "SessionGroup=")) { int group = (int)tor_parse_long(elt+strlen("SessionGroup="), 10, 0, INT_MAX, &ok, NULL); - if (!ok) { + if (!ok || !allow_no_stream_options) { log_warn(LD_CONFIG, "Invalid %sPort option '%s'", portname, escaped(elt)); goto err; @@ -6038,6 +6470,26 @@ parse_port_config(smartlist_t *out, elt += 2; } + if (!strcasecmp(elt, "GroupWritable")) { + group_writable = !no; + has_used_unix_socket_only_option = 1; + continue; + } else if (!strcasecmp(elt, "WorldWritable")) { + world_writable = !no; + has_used_unix_socket_only_option = 1; + continue; + } else if (!strcasecmp(elt, "RelaxDirModeCheck")) { + relax_dirmode_check = !no; + has_used_unix_socket_only_option = 1; + continue; + } + + if (allow_no_stream_options) { + log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'", + portname, escaped(elt)); + continue; + } + if (takes_hostnames) { if (!strcasecmp(elt, "IPv4Traffic")) { ipv4_traffic = ! no; @@ -6074,6 +6526,9 @@ parse_port_config(smartlist_t *out, } else if (!strcasecmp(elt, "PreferSOCKSNoAuth")) { prefer_no_auth = ! no; continue; + } else if (!strcasecmp(elt, "KeepAliveIsolateSOCKSAuth")) { + socks_iso_keep_alive = ! no; + continue; } if (!strcasecmpend(elt, "s")) @@ -6113,6 +6568,20 @@ parse_port_config(smartlist_t *out, goto err; } + if ( has_used_unix_socket_only_option && ! unix_socket_path) { + log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable, " + "WorldWritable, or RelaxDirModeCheck, but it is not a " + "unix socket.", portname); + goto err; + } + + if (!(isolation & ISO_SOCKSAUTH) && socks_iso_keep_alive) { + log_warn(LD_CONFIG, "You have a %sPort entry with both " + "NoIsolateSOCKSAuth and KeepAliveIsolateSOCKSAuth set.", + portname); + goto err; + } + if (out && port) { size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; port_cfg_t *cfg = port_cfg_new(namelen); @@ -6126,6 +6595,9 @@ parse_port_config(smartlist_t *out, cfg->port = port; } cfg->type = listener_type; + cfg->is_world_writable = world_writable; + cfg->is_group_writable = group_writable; + cfg->relax_dirmode_check = relax_dirmode_check; cfg->entry_cfg.isolation_flags = isolation; cfg->entry_cfg.session_group = sessiongroup; cfg->server_cfg.no_advertise = no_advertise; @@ -6144,6 +6616,7 @@ parse_port_config(smartlist_t *out, cfg->entry_cfg.socks_prefer_no_auth = prefer_no_auth; if (! (isolation & ISO_SOCKSAUTH)) cfg->entry_cfg.socks_prefer_no_auth = 1; + cfg->entry_cfg.socks_iso_keep_alive = socks_iso_keep_alive; smartlist_add(out, cfg); } @@ -6203,7 +6676,8 @@ count_real_listeners(const smartlist_t *ports, int listenertype) **/ static int parse_ports(or_options_t *options, int validate_only, - char **msg, int *n_ports_out) + char **msg, int *n_ports_out, + int *world_writable_control_socket) { smartlist_t *ports; int retval = -1; @@ -6212,12 +6686,14 @@ parse_ports(or_options_t *options, int validate_only, *n_ports_out = 0; + const unsigned gw_flag = options->SocksSocketsGroupWritable ? + CL_PORT_DFLT_GROUP_WRITABLE : 0; if (parse_port_config(ports, options->SocksPort_lines, options->SocksListenAddress, "Socks", CONN_TYPE_AP_LISTENER, "127.0.0.1", 9050, CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR| - CL_PORT_TAKES_HOSTNAMES) < 0) { + CL_PORT_TAKES_HOSTNAMES|gw_flag) < 0) { *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); goto err; } @@ -6246,12 +6722,15 @@ parse_ports(or_options_t *options, int validate_only, goto err; } { - unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL; + unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS | + CL_PORT_WARN_NONLOCAL; const int any_passwords = (options->HashedControlPassword || options->HashedControlSessionPassword || options->CookieAuthentication); if (! any_passwords) control_port_flags |= CL_PORT_FORBID_NONLOCAL; + if (options->ControlSocketsGroupWritable) + control_port_flags |= CL_PORT_DFLT_GROUP_WRITABLE; if (parse_port_config(ports, options->ControlPort_lines, @@ -6299,10 +6778,13 @@ parse_ports(or_options_t *options, int validate_only, } } - if (check_server_ports(ports, options) < 0) { + int n_low_ports = 0; + if (check_server_ports(ports, options, &n_low_ports) < 0) { *msg = tor_strdup("Misconfigured server ports"); goto err; } + if (have_low_ports < 0) + have_low_ports = (n_low_ports > 0); *n_ports_out = smartlist_len(ports); @@ -6327,6 +6809,16 @@ parse_ports(or_options_t *options, int validate_only, options->ExtORPort_set = !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER); + if (world_writable_control_socket) { + SMARTLIST_FOREACH(ports, port_cfg_t *, p, + if (p->type == CONN_TYPE_CONTROL_LISTENER && + p->is_unix_addr && + p->is_world_writable) { + *world_writable_control_socket = 1; + break; + }); + } + if (!validate_only) { if (configured_ports) { SMARTLIST_FOREACH(configured_ports, @@ -6346,10 +6838,12 @@ parse_ports(or_options_t *options, int validate_only, } /** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal - * consistency and warn as appropriate. */ + * consistency and warn as appropriate. Set *<b>n_low_ports_out</b> to the + * number of sub-1024 ports we will be binding. */ static int check_server_ports(const smartlist_t *ports, - const or_options_t *options) + const or_options_t *options, + int *n_low_ports_out) { int n_orport_advertised = 0; int n_orport_advertised_ipv4 = 0; @@ -6412,23 +6906,31 @@ check_server_ports(const smartlist_t *ports, r = -1; } - if (n_low_port && options->AccountingMax) { + if (n_low_port && options->AccountingMax && + (!have_capability_support() || options->KeepBindCapabilities == 0)) { + const char *extra = ""; + if (options->KeepBindCapabilities == 0 && have_capability_support()) + extra = ", and you have disabled KeepBindCapabilities."; log_warn(LD_CONFIG, "You have set AccountingMax to use hibernation. You have also " - "chosen a low DirPort or OrPort. This combination can make Tor stop " + "chosen a low DirPort or OrPort%s." + "This combination can make Tor stop " "working when it tries to re-attach the port after a period of " "hibernation. Please choose a different port or turn off " "hibernation unless you know this combination will work on your " - "platform."); + "platform.", extra); } + if (n_low_ports_out) + *n_low_ports_out = n_low_port; + return r; } /** Return a list of port_cfg_t for client ports parsed from the * options. */ -const smartlist_t * -get_configured_ports(void) +MOCK_IMPL(const smartlist_t *, +get_configured_ports,(void)) { if (!configured_ports) configured_ports = smartlist_new(); @@ -6500,9 +7002,8 @@ get_first_listener_addrport_string(int listener_type) int get_first_advertised_port_by_type_af(int listener_type, int address_family) { - if (!configured_ports) - return 0; - SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { + const smartlist_t *conf_ports = get_configured_ports(); + SMARTLIST_FOREACH_BEGIN(conf_ports, const port_cfg_t *, cfg) { if (cfg->type == listener_type && !cfg->server_cfg.no_advertise && (tor_addr_family(&cfg->addr) == address_family || @@ -6692,7 +7193,6 @@ get_num_cpus(const or_options_t *options) static void init_libevent(const or_options_t *options) { - const char *badness=NULL; tor_libevent_cfg cfg; tor_assert(options); @@ -6713,17 +7213,6 @@ init_libevent(const or_options_t *options) tor_libevent_initialize(&cfg); suppress_libevent_log_msg(NULL); - - tor_check_libevent_version(tor_libevent_get_method(), - server_mode(get_options()), - &badness); - if (badness) { - const char *v = tor_libevent_get_version_str(); - const char *m = tor_libevent_get_method(); - control_event_general_status(LOG_WARN, - "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO", - v, m, badness); - } } /** Return a newly allocated string holding a filename relative to the data @@ -6924,15 +7413,67 @@ getinfo_helper_config(control_connection_t *conn, smartlist_free(sl); } else if (!strcmp(question, "config/defaults")) { smartlist_t *sl = smartlist_new(); - int i; + int i, dirauth_lines_seen = 0, fallback_lines_seen = 0; for (i = 0; option_vars_[i].name; ++i) { const config_var_t *var = &option_vars_[i]; if (var->initvalue != NULL) { - char *val = esc_for_log(var->initvalue); - smartlist_add_asprintf(sl, "%s %s\n",var->name,val); - tor_free(val); + if (strcmp(option_vars_[i].name, "DirAuthority") == 0) { + /* + * Count dirauth lines we have a default for; we'll use the + * count later to decide whether to add the defaults manually + */ + ++dirauth_lines_seen; + } + if (strcmp(option_vars_[i].name, "FallbackDir") == 0) { + /* + * Similarly count fallback lines, so that we can decided later + * to add the defaults manually. + */ + ++fallback_lines_seen; + } + char *val = esc_for_log(var->initvalue); + smartlist_add_asprintf(sl, "%s %s\n",var->name,val); + tor_free(val); + } + } + + if (dirauth_lines_seen == 0) { + /* + * We didn't see any directory authorities with default values, + * so add the list of default authorities manually. + */ + const char **i; + + /* + * default_authorities is defined earlier in this file and + * is a const char ** NULL-terminated array of dirauth config + * lines. + */ + for (i = default_authorities; *i != NULL; ++i) { + char *val = esc_for_log(*i); + smartlist_add_asprintf(sl, "DirAuthority %s\n", val); + tor_free(val); } } + + if (fallback_lines_seen == 0 && + get_options()->UseDefaultFallbackDirs == 1) { + /* + * We didn't see any explicitly configured fallback mirrors, + * so add the defaults to the list manually. + * + * default_fallbacks is included earlier in this file and + * is a const char ** NULL-terminated array of fallback config lines. + */ + const char **i; + + for (i = default_fallbacks; *i != NULL; ++i) { + char *val = esc_for_log(*i); + smartlist_add_asprintf(sl, "FallbackDir %s\n", val); + tor_free(val); + } + } + *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); @@ -7075,8 +7616,7 @@ init_cookie_authentication(const char *fname, const char *header, /* Generate the cookie */ *cookie_out = tor_malloc(cookie_len); - if (crypto_rand((char *)*cookie_out, cookie_len) < 0) - goto done; + crypto_rand((char *)*cookie_out, cookie_len); /* Create the string that should be written on the file. */ memcpy(cookie_file_str, header, strlen(header)); diff --git a/src/or/config.h b/src/or/config.h index b064f05321..02121cf95c 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,9 +14,13 @@ #include "testsupport.h" -const char *get_dirportfrontpage(void); -MOCK_DECL(const or_options_t *,get_options,(void)); -or_options_t *get_options_mutable(void); +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(DARWIN) +#define KERNEL_MAY_SUPPORT_IPFW +#endif + +MOCK_DECL(const char*, get_dirportfrontpage, (void)); +MOCK_DECL(const or_options_t *, get_options, (void)); +MOCK_DECL(or_options_t *, get_options_mutable, (void)); int set_options(or_options_t *new_val, char **msg); void config_free_all(void); const char *safe_str_client(const char *address); @@ -61,6 +65,10 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options, * get_datadir_fname2_suffix. */ #define get_datadir_fname2(sub1,sub2) \ get_datadir_fname2_suffix((sub1), (sub2), NULL) +/** Return a newly allocated string containing datadir/sub1/sub2 relative to + * opts. See get_datadir_fname2_suffix. */ +#define options_get_datadir_fname2(opts,sub1,sub2) \ + options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL) /** Return a newly allocated string containing datadir/sub1suffix. See * get_datadir_fname2_suffix. */ #define get_datadir_fname_suffix(sub1, suffix) \ @@ -72,7 +80,7 @@ int write_to_data_subdir(const char* subdir, const char* fname, int get_num_cpus(const or_options_t *options); -const smartlist_t *get_configured_ports(void); +MOCK_DECL(const smartlist_t *,get_configured_ports,(void)); int get_first_advertised_port_by_type_af(int listener_type, int address_family); #define get_primary_or_port() \ @@ -91,7 +99,6 @@ int getinfo_helper_config(control_connection_t *conn, const char *question, char **answer, const char **errmsg); -const char *tor_get_digests(void); uint32_t get_effective_bwrate(const or_options_t *options); uint32_t get_effective_bwburst(const or_options_t *options); @@ -133,10 +140,23 @@ smartlist_t *get_options_from_transport_options_line(const char *line, smartlist_t *get_options_for_server_transport(const char *transport); #ifdef CONFIG_PRIVATE + +#define CL_PORT_NO_STREAM_OPTIONS (1u<<0) +#define CL_PORT_WARN_NONLOCAL (1u<<1) +#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) +#define CL_PORT_SERVER_OPTIONS (1u<<3) +#define CL_PORT_FORBID_NONLOCAL (1u<<4) +#define CL_PORT_TAKES_HOSTNAMES (1u<<5) +#define CL_PORT_IS_UNIXSOCKET (1u<<6) +#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7) + +STATIC int options_act(const or_options_t *old_options); #ifdef TOR_UNIT_TESTS extern struct config_format_t options_format; #endif +STATIC port_cfg_t *port_cfg_new(size_t namelen); +STATIC void port_cfg_free(port_cfg_t *port); STATIC void or_options_free(or_options_t *options); STATIC int options_validate(or_options_t *old_options, or_options_t *options, @@ -145,6 +165,24 @@ STATIC int options_validate(or_options_t *old_options, STATIC int parse_transport_line(const or_options_t *options, const char *line, int validate_only, int server); +STATIC int consider_adding_dir_servers(const or_options_t *options, + const or_options_t *old_options); +STATIC void add_default_trusted_dir_authorities(dirinfo_type_t type); +MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void)); +STATIC int parse_dir_authority_line(const char *line, + dirinfo_type_t required_type, + int validate_only); +STATIC int parse_dir_fallback_line(const char *line, int validate_only); +STATIC int have_enough_mem_for_dircache(const or_options_t *options, + size_t total_mem, char **msg); +STATIC int parse_port_config(smartlist_t *out, + const config_line_t *ports, + const config_line_t *listenaddrs, + const char *portname, + int listener_type, + const char *defaultaddr, + int defaultport, + const unsigned flags); #endif #endif diff --git a/src/or/config_codedigest.c b/src/or/config_codedigest.c deleted file mode 100644 index 86d14bacef..0000000000 --- a/src/or/config_codedigest.c +++ /dev/null @@ -1,13 +0,0 @@ - -const char *tor_get_digests(void); - -/** Return a string describing the digest of the source files in src/or/ - */ -const char * -tor_get_digests(void) -{ - return "" -#include "or_sha1.i" - ; -} - diff --git a/src/or/confparse.c b/src/or/confparse.c index ac21df25cb..4f446d07c3 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -1,9 +1,16 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file confparse.c + * + * \brief Back-end for parsing and generating key-value files, used to + * implement the torrc file format and the state file. + */ + #include "or.h" #include "confparse.h" #include "routerset.h" diff --git a/src/or/confparse.h b/src/or/confparse.h index 83c0f75b52..885c615202 100644 --- a/src/or/confparse.h +++ b/src/or/confparse.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_CONFPARSE_H diff --git a/src/or/connection.c b/src/or/connection.c index 721ee20d27..4fbbaf1abd 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -19,6 +19,7 @@ */ #define TOR_CHANNEL_INTERNAL_ #define CONNECTION_PRIVATE +#include "backtrace.h" #include "channel.h" #include "channeltls.h" #include "circuitbuild.h" @@ -37,6 +38,7 @@ #include "ext_orport.h" #include "geoip.h" #include "main.h" +#include "nodelist.h" #include "policies.h" #include "reasons.h" #include "relay.h" @@ -44,8 +46,10 @@ #include "rendcommon.h" #include "rephist.h" #include "router.h" +#include "routerlist.h" #include "transports.h" #include "routerparse.h" +#include "sandbox.h" #include "transports.h" #ifdef USE_BUFFEREVENTS @@ -586,6 +590,13 @@ connection_free_(connection_t *conn) control_connection_t *control_conn = TO_CONTROL_CONN(conn); tor_free(control_conn->safecookie_client_hash); tor_free(control_conn->incoming_cmd); + if (control_conn->ephemeral_onion_services) { + SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(control_conn->ephemeral_onion_services); + } } /* Probably already freed by connection_free. */ @@ -647,8 +658,8 @@ connection_free_(connection_t *conn) /** Make sure <b>conn</b> isn't in any of the global conn lists; then free it. */ -void -connection_free(connection_t *conn) +MOCK_IMPL(void, +connection_free,(connection_t *conn)) { if (!conn) return; @@ -671,6 +682,13 @@ connection_free(connection_t *conn) if (conn->type == CONN_TYPE_CONTROL) { connection_control_closed(TO_CONTROL_CONN(conn)); } +#if 1 + /* DEBUGGING */ + if (conn->type == CONN_TYPE_AP) { + connection_ap_warn_and_unmark_if_pending_circ(TO_ENTRY_CONN(conn), + "connection_free"); + } +#endif connection_unregister_events(conn); connection_free_(conn); } @@ -972,7 +990,7 @@ unix_socket_purpose_to_string(int purpose) * <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */ static int check_location_for_unix_socket(const or_options_t *options, const char *path, - int purpose) + int purpose, const port_cfg_t *port) { int r = -1; char *p = NULL; @@ -987,13 +1005,20 @@ check_location_for_unix_socket(const or_options_t *options, const char *path, goto done; } - if ((purpose == UNIX_SOCKET_PURPOSE_CONTROL_SOCKET && - options->ControlSocketsGroupWritable) || - (purpose == UNIX_SOCKET_PURPOSE_SOCKS_SOCKET && - options->SocksSocketsGroupWritable)) { + if (port->is_world_writable) { + /* World-writable sockets can go anywhere. */ + r = 0; + goto done; + } + + if (port->is_group_writable) { flags |= CPD_GROUP_OK; } + if (port->relax_dirmode_check) { + flags |= CPD_RELAX_DIRMODE_CHECK; + } + if (check_private_dir(p, flags, options->User) < 0) { char *escpath, *escdir; escpath = esc_for_log(path); @@ -1004,7 +1029,7 @@ check_location_for_unix_socket(const or_options_t *options, const char *path, "who can list a socket can connect to it, so Tor is being " "careful.)", unix_socket_purpose_to_string(purpose), escpath, escdir, - options->ControlSocketsGroupWritable ? " and group" : ""); + port->is_group_writable ? " and group" : ""); tor_free(escpath); tor_free(escdir); goto done; @@ -1041,6 +1066,31 @@ make_socket_reuseable(tor_socket_t sock) #endif } +#ifdef _WIN32 +/** Tell the Windows TCP stack to prevent other applications from receiving + * traffic from tor's open ports. Return 0 on success, -1 on failure. */ +static int +make_win32_socket_exclusive(tor_socket_t sock) +{ +#ifdef SO_EXCLUSIVEADDRUSE + int one=1; + + /* Any socket that sets REUSEADDR on win32 can bind to a port _even when + * somebody else already has it bound_, and _even if the original socket + * didn't set REUSEADDR_. Use EXCLUSIVEADDRUSE to prevent this port-stealing + * on win32. */ + if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void*) &one, + (socklen_t)sizeof(one))) { + return -1; + } + return 0; +#else + (void) sock; + return 0; +#endif +} +#endif + /** Max backlog to pass to listen. We start at */ static int listen_limit = INT_MAX; @@ -1078,6 +1128,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, connection_t *conn = NULL; tor_socket_t s = TOR_INVALID_SOCKET; /* the socket we're going to make */ or_options_t const *options = get_options(); + (void) options; /* Windows doesn't use this. */ #if defined(HAVE_PWD_H) && defined(HAVE_SYS_UN_H) const struct passwd *pw = NULL; #endif @@ -1086,11 +1137,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, static int global_next_session_group = SESSION_GROUP_FIRST_AUTO; tor_addr_t addr; - if (get_n_open_sockets() >= options->ConnLimit_-1) { - warn_too_many_conns(); - return NULL; - } - if (listensockaddr->sa_family == AF_INET || listensockaddr->sa_family == AF_INET6) { int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER); @@ -1098,7 +1144,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, start_reading = 1; tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); - log_notice(LD_NET, "Opening %s on %s", conn_type_to_string(type), fmt_addrport(&addr, usePort)); @@ -1106,8 +1151,13 @@ connection_listener_new(const struct sockaddr *listensockaddr, is_stream ? SOCK_STREAM : SOCK_DGRAM, is_stream ? IPPROTO_TCP: IPPROTO_UDP); if (!SOCKET_OK(s)) { - log_warn(LD_NET, "Socket creation failed: %s", - tor_socket_strerror(tor_socket_errno(-1))); + int e = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(e)) { + warn_too_many_conns(); + } else { + log_warn(LD_NET, "Socket creation failed: %s", + tor_socket_strerror(e)); + } goto err; } @@ -1117,11 +1167,20 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_socket_strerror(errno)); } -#if defined USE_TRANSPARENT && defined(IP_TRANSPARENT) +#ifdef _WIN32 + if (make_win32_socket_exclusive(s) < 0) { + log_warn(LD_NET, "Error setting SO_EXCLUSIVEADDRUSE flag on %s: %s", + conn_type_to_string(type), + tor_socket_strerror(errno)); + } +#endif + +#if defined(USE_TRANSPARENT) && defined(IP_TRANSPARENT) if (options->TransProxyType_parsed == TPT_TPROXY && type == CONN_TYPE_AP_TRANS_LISTENER) { int one = 1; - if (setsockopt(s, SOL_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) { + if (setsockopt(s, SOL_IP, IP_TRANSPARENT, (void*)&one, + (socklen_t)sizeof(one)) < 0) { const char *extra = ""; int e = tor_socket_errno(s); if (e == EPERM) @@ -1135,16 +1194,11 @@ connection_listener_new(const struct sockaddr *listensockaddr, #ifdef IPV6_V6ONLY if (listensockaddr->sa_family == AF_INET6) { -#ifdef _WIN32 - /* In Redmond, this kind of thing passes for standards-conformance. */ - DWORD one = 1; -#else int one = 1; -#endif /* We need to set IPV6_V6ONLY so that this socket can't get used for * IPv4 connections. */ if (setsockopt(s,IPPROTO_IPV6, IPV6_V6ONLY, - (void*)&one, sizeof(one)) < 0) { + (void*)&one, (socklen_t)sizeof(one)) < 0) { int e = tor_socket_errno(s); log_warn(LD_NET, "Error setting IPV6_V6ONLY flag: %s", tor_socket_strerror(e)); @@ -1198,7 +1252,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, if (check_location_for_unix_socket(options, address, (type == CONN_TYPE_CONTROL_LISTENER) ? UNIX_SOCKET_PURPOSE_CONTROL_SOCKET : - UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { + UNIX_SOCKET_PURPOSE_SOCKS_SOCKET, port_cfg) < 0) { goto err; } @@ -1215,7 +1269,12 @@ connection_listener_new(const struct sockaddr *listensockaddr, s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); if (! SOCKET_OK(s)) { - log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno)); + int e = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(e)) { + warn_too_many_conns(); + } else { + log_warn(LD_NET,"Socket creation failed: %s.", strerror(e)); + } goto err; } @@ -1229,11 +1288,16 @@ connection_listener_new(const struct sockaddr *listensockaddr, #ifdef HAVE_PWD_H if (options->User) { pw = tor_getpwnam(options->User); + struct stat st; if (pw == NULL) { log_warn(LD_NET,"Unable to chown() %s socket: user %s not found.", address, options->User); goto err; - } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) { + } else if (fstat(s, &st) == 0 && + st.st_uid == pw->pw_uid && st.st_gid == pw->pw_gid) { + /* No change needed */ + } else if (chown(sandbox_intern_string(address), + pw->pw_uid, pw->pw_gid) < 0) { log_warn(LD_NET,"Unable to chown() %s socket: %s.", address, strerror(errno)); goto err; @@ -1241,24 +1305,26 @@ connection_listener_new(const struct sockaddr *listensockaddr, } #endif - if ((type == CONN_TYPE_CONTROL_LISTENER && - options->ControlSocketsGroupWritable) || - (type == CONN_TYPE_AP_LISTENER && - options->SocksSocketsGroupWritable)) { - /* We need to use chmod; fchmod doesn't work on sockets on all - * platforms. */ - if (chmod(address, 0660) < 0) { - log_warn(LD_FS,"Unable to make %s group-writable.", address); - goto err; + { + unsigned mode; + const char *status; + struct stat st; + if (port_cfg->is_world_writable) { + mode = 0666; + status = "world-writable"; + } else if (port_cfg->is_group_writable) { + mode = 0660; + status = "group-writable"; + } else { + mode = 0600; + status = "private"; } - } else if ((type == CONN_TYPE_CONTROL_LISTENER && - !(options->ControlSocketsGroupWritable)) || - (type == CONN_TYPE_AP_LISTENER && - !(options->SocksSocketsGroupWritable))) { /* We need to use chmod; fchmod doesn't work on sockets on all * platforms. */ - if (chmod(address, 0600) < 0) { - log_warn(LD_FS,"Unable to make %s group-writable.", address); + if (fstat(s, &st) == 0 && (st.st_mode & 0777) == mode) { + /* no change needed */ + } else if (chmod(sandbox_intern_string(address), mode) < 0) { + log_warn(LD_FS,"Unable to make %s %s.", address, status); goto err; } } @@ -1407,7 +1473,7 @@ static int connection_handle_listener_read(connection_t *conn, int new_type) { tor_socket_t news; /* the new socket */ - connection_t *newconn; + connection_t *newconn = 0; /* information about the remote peer when connecting to other routers */ struct sockaddr_storage addrbuf; struct sockaddr *remote = (struct sockaddr*)&addrbuf; @@ -1422,8 +1488,8 @@ connection_handle_listener_read(connection_t *conn, int new_type) if (!SOCKET_OK(news)) { /* accept() error */ int e = tor_socket_errno(conn->s); if (ERRNO_IS_ACCEPT_EAGAIN(e)) { - return 0; /* he hung up before we could accept(). that's fine. */ - } else if (ERRNO_IS_ACCEPT_RESOURCE_LIMIT(e)) { + return 0; /* they hung up before we could accept(). that's fine. */ + } else if (ERRNO_IS_RESOURCE_LIMIT(e)) { warn_too_many_conns(); return 0; } @@ -1582,6 +1648,8 @@ connection_init_accepted_conn(connection_t *conn, break; case CONN_TYPE_AP_TRANS_LISTENER: TO_ENTRY_CONN(conn)->is_transparent_ap = 1; + /* XXXX028 -- is this correct still, with the addition of + * pending_entry_connections ? */ conn->state = AP_CONN_STATE_CIRCUIT_WAIT; return connection_ap_process_transparent(TO_ENTRY_CONN(conn)); case CONN_TYPE_AP_NATD_LISTENER: @@ -1601,13 +1669,18 @@ connection_init_accepted_conn(connection_t *conn, return 0; } -static int -connection_connect_sockaddr(connection_t *conn, +/** Take conn, make a nonblocking socket; try to connect to + * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if + * applicable put your best guess about errno into *<b>socket_error</b>. + * If connected return 1, if EAGAIN return 0. + */ +MOCK_IMPL(STATIC int, +connection_connect_sockaddr,(connection_t *conn, const struct sockaddr *sa, socklen_t sa_len, const struct sockaddr *bindaddr, socklen_t bindaddr_len, - int *socket_error) + int *socket_error)) { tor_socket_t s; int inprogress = 0; @@ -1617,12 +1690,6 @@ connection_connect_sockaddr(connection_t *conn, tor_assert(sa); tor_assert(socket_error); - if (get_n_open_sockets() >= get_options()->ConnLimit_-1) { - warn_too_many_conns(); - *socket_error = SOCK_ERRNO(ENOBUFS); - return -1; - } - if (get_options()->DisableNetwork) { /* We should never even try to connect anyplace if DisableNetwork is set. * Warn if we do, and refuse to make the connection. */ @@ -1640,9 +1707,13 @@ connection_connect_sockaddr(connection_t *conn, s = tor_open_socket_nonblocking(protocol_family, SOCK_STREAM, proto); if (! SOCKET_OK(s)) { - *socket_error = tor_socket_errno(-1); - log_warn(LD_NET,"Error creating network socket: %s", - tor_socket_strerror(*socket_error)); + *socket_error = tor_socket_errno(s); + if (ERRNO_IS_RESOURCE_LIMIT(*socket_error)) { + warn_too_many_conns(); + } else { + log_warn(LD_NET,"Error creating network socket: %s", + tor_socket_strerror(*socket_error)); + } return -1; } @@ -1692,11 +1763,87 @@ connection_connect_sockaddr(connection_t *conn, return inprogress ? 0 : 1; } +/* Log a message if connection attempt is made when IPv4 or IPv6 is disabled. + * Log a less severe message if we couldn't conform to ClientPreferIPv6ORPort + * or ClientPreferIPv6ORPort. */ +static void +connection_connect_log_client_use_ip_version(const connection_t *conn) +{ + const or_options_t *options = get_options(); + + /* Only clients care about ClientUseIPv4/6, bail out early on servers, and + * on connections we don't care about */ + if (server_mode(options) || !conn || conn->type == CONN_TYPE_EXIT) { + return; + } + + /* We're only prepared to log OR and DIR connections here */ + if (conn->type != CONN_TYPE_OR && conn->type != CONN_TYPE_DIR) { + return; + } + + const int must_ipv4 = !fascist_firewall_use_ipv6(options); + const int must_ipv6 = (options->ClientUseIPv4 == 0); + const int pref_ipv6 = (conn->type == CONN_TYPE_OR + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + tor_addr_t real_addr; + tor_addr_make_null(&real_addr, AF_UNSPEC); + + /* OR conns keep the original address in real_addr, as addr gets overwritten + * with the descriptor address */ + if (conn->type == CONN_TYPE_OR) { + const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn); + tor_addr_copy(&real_addr, &or_conn->real_addr); + } else if (conn->type == CONN_TYPE_DIR) { + tor_addr_copy(&real_addr, &conn->addr); + } + + /* Check if we broke a mandatory address family restriction */ + if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6) + || (must_ipv6 && tor_addr_family(&real_addr) == AF_INET)) { + static int logged_backtrace = 0; + log_info(LD_BUG, "Outgoing %s connection to %s violated ClientUseIPv%s 0.", + conn->type == CONN_TYPE_OR ? "OR" : "Dir", + fmt_addr(&real_addr), + options->ClientUseIPv4 == 0 ? "4" : "6"); + if (!logged_backtrace) { + log_backtrace(LOG_INFO, LD_BUG, "Address came from"); + logged_backtrace = 1; + } + } + + /* Bridges are allowed to break IPv4/IPv6 ORPort preferences to connect to + * the node's configured address when ClientPreferIPv6ORPort is auto */ + if (options->UseBridges && conn->type == CONN_TYPE_OR + && options->ClientPreferIPv6ORPort == -1) { + return; + } + + /* Check if we couldn't satisfy an address family preference */ + if ((!pref_ipv6 && tor_addr_family(&real_addr) == AF_INET6) + || (pref_ipv6 && tor_addr_family(&real_addr) == AF_INET)) { + log_info(LD_NET, "Outgoing connection to %s doesn't satisfy " + "ClientPreferIPv6%sPort %d, with ClientUseIPv4 %d, and " + "fascist_firewall_use_ipv6 %d (ClientUseIPv6 %d and UseBridges " + "%d).", + fmt_addr(&real_addr), + conn->type == CONN_TYPE_OR ? "OR" : "Dir", + conn->type == CONN_TYPE_OR ? options->ClientPreferIPv6ORPort + : options->ClientPreferIPv6DirPort, + options->ClientUseIPv4, fascist_firewall_use_ipv6(options), + options->ClientUseIPv6, options->UseBridges); + } +} + /** Take conn, make a nonblocking socket; try to connect to - * addr:port (they arrive in *host order*). If fail, return -1 and if + * addr:port (port arrives in *host order*). If fail, return -1 and if * applicable put your best guess about errno into *<b>socket_error</b>. * Else assign s to conn-\>s: if connected return 1, if EAGAIN return 0. * + * addr:port can be different to conn->addr:conn->port if connecting through + * a proxy. + * * address is used to make the logs useful. * * On success, add conn to the list of polled connections. @@ -1713,6 +1860,10 @@ connection_connect(connection_t *conn, const char *address, const or_options_t *options = get_options(); int protocol_family; + /* Log if we didn't stick to ClientUseIPv4/6 or ClientPreferIPv6OR/DirPort + */ + connection_connect_log_client_use_ip_version(conn); + if (tor_addr_family(addr) == AF_INET6) protocol_family = PF_INET6; else @@ -2368,6 +2519,15 @@ retry_listener_ports(smartlist_t *old_conns, if (port->server_cfg.no_listen) continue; +#ifndef _WIN32 + /* We don't need to be root to create a UNIX socket, so defer until after + * setuid. */ + const or_options_t *options = get_options(); + if (port->is_unix_addr && !geteuid() && (options->User) && + strcmp(options->User, "root")) + continue; +#endif + if (port->is_unix_addr) { listensockaddr = (struct sockaddr *) create_unix_sockaddr(port->unix_addr, @@ -3572,7 +3732,7 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read, } /* Call even if result is 0, since the global read bucket may - * have reached 0 on a different conn, and this guy needs to + * have reached 0 on a different conn, and this connection needs to * know to stop reading. */ connection_consider_empty_read_buckets(conn); if (n_written > 0 && connection_is_writing(conn)) @@ -3774,7 +3934,7 @@ connection_fetch_from_buf_line(connection_t *conn, char *data, } } -/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or +/** As fetch_from_buf_http, but fetches from a connection's input buffer_t or * its bufferevent as appropriate. */ int connection_fetch_from_buf_http(connection_t *conn, @@ -4068,7 +4228,7 @@ connection_handle_write_impl(connection_t *conn, int force) } /* Call even if result is 0, since the global write bucket may - * have reached 0 on a different conn, and this guy needs to + * have reached 0 on a different conn, and this connection needs to * know to stop writing. */ connection_consider_empty_write_buckets(conn); if (n_read > 0 && connection_is_reading(conn)) @@ -4193,55 +4353,35 @@ connection_write_to_buf_impl_,(const char *string, size_t len, conn->outbuf_flushlen += buf_datalen(conn->outbuf) - old_datalen; } else { conn->outbuf_flushlen += len; - - /* Should we try flushing the outbuf now? */ - if (conn->in_flushed_some) { - /* Don't flush the outbuf when the reason we're writing more stuff is - * _because_ we flushed the outbuf. That's unfair. */ - return; - } - - if (conn->type == CONN_TYPE_CONTROL && - !connection_is_rate_limited(conn) && - conn->outbuf_flushlen-len < 1<<16 && - conn->outbuf_flushlen >= 1<<16) { - /* just try to flush all of it */ - } else - return; /* no need to try flushing */ - - if (connection_handle_write(conn, 0) < 0) { - if (!conn->marked_for_close) { - /* this connection is broken. remove it. */ - log_warn(LD_BUG, "unhandled error on write for " - "conn (type %d, fd %d); removing", - conn->type, (int)conn->s); - tor_fragile_assert(); - /* do a close-immediate here, so we don't try to flush */ - connection_close_immediate(conn); - } - return; - } } } +/** Return a connection_t * from get_connection_array() that satisfies test on + * var, and that is not marked for close. */ +#define CONN_GET_TEMPLATE(var, test) \ + STMT_BEGIN \ + smartlist_t *conns = get_connection_array(); \ + SMARTLIST_FOREACH(conns, connection_t *, var, \ + { \ + if (var && (test) && !var->marked_for_close) \ + return var; \ + }); \ + return NULL; \ + STMT_END + /** Return a connection with given type, address, port, and purpose; - * or NULL if no such connection exists. */ -connection_t * -connection_get_by_type_addr_port_purpose(int type, + * or NULL if no such connection exists (or if all such connections are marked + * for close). */ +MOCK_IMPL(connection_t *, +connection_get_by_type_addr_port_purpose,(int type, const tor_addr_t *addr, uint16_t port, - int purpose) + int purpose)) { - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && + CONN_GET_TEMPLATE(conn, + (conn->type == type && tor_addr_eq(&conn->addr, addr) && conn->port == port && - conn->purpose == purpose && - !conn->marked_for_close) - return conn; - }); - return NULL; + conn->purpose == purpose)); } /** Return the stream with id <b>id</b> if it is not already marked for @@ -4250,13 +4390,7 @@ connection_get_by_type_addr_port_purpose(int type, connection_t * connection_get_by_global_id(uint64_t id) { - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->global_identifier == id) - return conn; - }); - return NULL; + CONN_GET_TEMPLATE(conn, conn->global_identifier == id); } /** Return a connection of type <b>type</b> that is not marked for close. @@ -4264,13 +4398,7 @@ connection_get_by_global_id(uint64_t id) connection_t * connection_get_by_type(int type) { - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && !conn->marked_for_close) - return conn; - }); - return NULL; + CONN_GET_TEMPLATE(conn, conn->type == type); } /** Return a connection of type <b>type</b> that is in state <b>state</b>, @@ -4279,13 +4407,7 @@ connection_get_by_type(int type) connection_t * connection_get_by_type_state(int type, int state) { - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH(conns, connection_t *, conn, - { - if (conn->type == type && conn->state == state && !conn->marked_for_close) - return conn; - }); - return NULL; + CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state); } /** Return a connection of type <b>type</b> that has rendquery equal @@ -4296,55 +4418,96 @@ connection_t * connection_get_by_type_state_rendquery(int type, int state, const char *rendquery) { - smartlist_t *conns = get_connection_array(); - tor_assert(type == CONN_TYPE_DIR || type == CONN_TYPE_AP || type == CONN_TYPE_EXIT); tor_assert(rendquery); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn->type == type && - !conn->marked_for_close && - (!state || state == conn->state)) { - if (type == CONN_TYPE_DIR && + CONN_GET_TEMPLATE(conn, + (conn->type == type && + (!state || state == conn->state)) && + ( + (type == CONN_TYPE_DIR && TO_DIR_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_DIR_CONN(conn)->rend_data->onion_address)) - return conn; - else if (CONN_IS_EDGE(conn) && + || + (CONN_IS_EDGE(conn) && TO_EDGE_CONN(conn)->rend_data && !rend_cmp_service_ids(rendquery, TO_EDGE_CONN(conn)->rend_data->onion_address)) - return conn; - } - } SMARTLIST_FOREACH_END(conn); - return NULL; + )); } -/** Return a directory connection (if any one exists) that is fetching - * the item described by <b>state</b>/<b>resource</b> */ -dir_connection_t * -connection_dir_get_by_purpose_and_resource(int purpose, - const char *resource) +/** Return a new smartlist of dir_connection_t * from get_connection_array() + * that satisfy conn_test on connection_t *conn_var, and dirconn_test on + * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not + * marked for close to be included in the list. */ +#define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test, \ + dirconn_var, dirconn_test) \ + STMT_BEGIN \ + smartlist_t *conns = get_connection_array(); \ + smartlist_t *dir_conns = smartlist_new(); \ + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) { \ + if (conn_var && (conn_test) \ + && conn_var->type == CONN_TYPE_DIR \ + && !conn_var->marked_for_close) { \ + dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var); \ + if (dirconn_var && (dirconn_test)) { \ + smartlist_add(dir_conns, dirconn_var); \ + } \ + } \ + } SMARTLIST_FOREACH_END(conn_var); \ + return dir_conns; \ + STMT_END + +/** Return a list of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>. If there are none, + * return an empty list. This list must be freed using smartlist_free, + * but the pointers in it must not be freed. + * Note that this list should not be cached, as the pointers in it can be + * freed if their connections close. */ +smartlist_t * +connection_dir_list_by_purpose_and_resource( + int purpose, + const char *resource) { - smartlist_t *conns = get_connection_array(); + DIR_CONN_LIST_TEMPLATE(conn, + conn->purpose == purpose, + dirconn, + 0 == strcmp_opt(resource, + dirconn->requested_resource)); +} - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - dir_connection_t *dirconn; - if (conn->type != CONN_TYPE_DIR || conn->marked_for_close || - conn->purpose != purpose) - continue; - dirconn = TO_DIR_CONN(conn); - if (dirconn->requested_resource == NULL) { - if (resource == NULL) - return dirconn; - } else if (resource) { - if (0 == strcmp(resource, dirconn->requested_resource)) - return dirconn; - } - } SMARTLIST_FOREACH_END(conn); +/** Return a list of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are + * none, return an empty list. This list must be freed using smartlist_free, + * but the pointers in it must not be freed. + * Note that this list should not be cached, as the pointers in it can be + * freed if their connections close. */ +smartlist_t * +connection_dir_list_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state) +{ + DIR_CONN_LIST_TEMPLATE(conn, + conn->purpose == purpose && conn->state == state, + dirconn, + 0 == strcmp_opt(resource, + dirconn->requested_resource)); +} - return NULL; +#undef DIR_CONN_LIST_TEMPLATE + +/** Return an arbitrary active OR connection that isn't <b>this_conn</b>. + * + * We use this to guess if we should tell the controller that we + * didn't manage to connect to any of our bridges. */ +static connection_t * +connection_get_another_active_or_conn(const or_connection_t *this_conn) +{ + CONN_GET_TEMPLATE(conn, + conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR); } /** Return 1 if there are any active OR connections apart from @@ -4355,23 +4518,18 @@ connection_dir_get_by_purpose_and_resource(int purpose, int any_other_active_or_conns(const or_connection_t *this_conn) { - smartlist_t *conns = get_connection_array(); - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { - if (conn == TO_CONN(this_conn)) { /* don't consider this conn */ - continue; - } - - if (conn->type == CONN_TYPE_OR && - !conn->marked_for_close) { - log_debug(LD_DIR, "%s: Found an OR connection: %s", - __func__, conn->address); - return 1; - } - } SMARTLIST_FOREACH_END(conn); + connection_t *conn = connection_get_another_active_or_conn(this_conn); + if (conn != NULL) { + log_debug(LD_DIR, "%s: Found an OR connection: %s", + __func__, conn->address); + return 1; + } return 0; } +#undef CONN_GET_TEMPLATE + /** Return 1 if <b>conn</b> is a listener conn, else return 0. */ int connection_is_listener(connection_t *conn) @@ -4440,25 +4598,12 @@ alloc_http_authenticator(const char *authenticator) /* an authenticator in Basic authentication * is just the string "username:password" */ const size_t authenticator_length = strlen(authenticator); - /* The base64_encode function needs a minimum buffer length - * of 66 bytes. */ - const size_t base64_authenticator_length = (authenticator_length/48+1)*66; + const size_t base64_authenticator_length = + base64_encode_size(authenticator_length, 0) + 1; char *base64_authenticator = tor_malloc(base64_authenticator_length); if (base64_encode(base64_authenticator, base64_authenticator_length, - authenticator, authenticator_length) < 0) { + authenticator, authenticator_length, 0) < 0) { tor_free(base64_authenticator); /* free and set to null */ - } else { - int i = 0, j = 0; - ssize_t len = strlen(base64_authenticator); - - /* remove all newline occurrences within the string */ - for (i=0; i < len; ++i) { - if ('\n' != base64_authenticator[i]) { - base64_authenticator[j] = base64_authenticator[i]; - ++j; - } - } - base64_authenticator[j]='\0'; } return base64_authenticator; } @@ -5040,3 +5185,34 @@ connection_free_all(void) #endif } +/** Log a warning, and possibly emit a control event, that <b>received</b> came + * at a skewed time. <b>trusted</b> indicates that the <b>source</b> was one + * that we had more faith in and therefore the warning level should have higher + * severity. + */ +void +clock_skew_warning(const connection_t *conn, long apparent_skew, int trusted, + log_domain_mask_t domain, const char *received, + const char *source) +{ + char dbuf[64]; + char *ext_source = NULL; + format_time_interval(dbuf, sizeof(dbuf), apparent_skew); + if (conn) + tor_asprintf(&ext_source, "%s:%s:%d", source, conn->address, conn->port); + else + ext_source = tor_strdup(source); + log_fn(trusted ? LOG_WARN : LOG_INFO, domain, + "Received %s with skewed time (%s): " + "It seems that our clock is %s by %s, or that theirs is %s%s. " + "Tor requires an accurate clock to work: please check your time, " + "timezone, and date settings.", received, ext_source, + apparent_skew > 0 ? "ahead" : "behind", dbuf, + apparent_skew > 0 ? "behind" : "ahead", + (!conn || trusted) ? "" : ", or they are sending us the wrong time"); + if (trusted) + control_event_general_status(LOG_WARN, "CLOCK_SKEW SKEW=%ld SOURCE=%s", + apparent_skew, ext_source); + tor_free(ext_source); +} + diff --git a/src/or/connection.h b/src/or/connection.h index d0a34ece5c..4835235fba 100644 --- a/src/or/connection.h +++ b/src/or/connection.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -28,7 +28,7 @@ listener_connection_t *listener_connection_new(int type, int socket_family); connection_t *connection_new(int type, int socket_family); void connection_link_connections(connection_t *conn_a, connection_t *conn_b); -void connection_free(connection_t *conn); +MOCK_DECL(void,connection_free,(connection_t *conn)); void connection_free_all(void); void connection_about_to_close_connection(connection_t *conn); void connection_close_immediate(connection_t *conn); @@ -146,12 +146,12 @@ static void connection_write_to_buf(const char *string, size_t len, /* DOCDOC connection_write_to_buf_zlib */ static void connection_write_to_buf_zlib(const char *string, size_t len, dir_connection_t *conn, int done); -static INLINE void +static inline void connection_write_to_buf(const char *string, size_t len, connection_t *conn) { connection_write_to_buf_impl_(string, len, conn, 0); } -static INLINE void +static inline void connection_write_to_buf_zlib(const char *string, size_t len, dir_connection_t *conn, int done) { @@ -163,7 +163,7 @@ static size_t connection_get_inbuf_len(connection_t *conn); /* DOCDOC connection_get_outbuf_len */ static size_t connection_get_outbuf_len(connection_t *conn); -static INLINE size_t +static inline size_t connection_get_inbuf_len(connection_t *conn) { IF_HAS_BUFFEREVENT(conn, { @@ -173,7 +173,7 @@ connection_get_inbuf_len(connection_t *conn) } } -static INLINE size_t +static inline size_t connection_get_outbuf_len(connection_t *conn) { IF_HAS_BUFFEREVENT(conn, { @@ -186,14 +186,57 @@ connection_get_outbuf_len(connection_t *conn) connection_t *connection_get_by_global_id(uint64_t id); connection_t *connection_get_by_type(int type); -connection_t *connection_get_by_type_addr_port_purpose(int type, - const tor_addr_t *addr, - uint16_t port, int purpose); +MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type, + const tor_addr_t *addr, + uint16_t port, int purpose)); connection_t *connection_get_by_type_state(int type, int state); connection_t *connection_get_by_type_state_rendquery(int type, int state, const char *rendquery); -dir_connection_t *connection_dir_get_by_purpose_and_resource( - int state, const char *resource); +smartlist_t *connection_dir_list_by_purpose_and_resource( + int purpose, + const char *resource); +smartlist_t *connection_dir_list_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state); + +#define CONN_LEN_AND_FREE_TEMPLATE(sl) \ + STMT_BEGIN \ + int len = smartlist_len(sl); \ + smartlist_free(sl); \ + return len; \ + STMT_END + +/** Return a count of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>. */ +static inline int +connection_dir_count_by_purpose_and_resource( + int purpose, + const char *resource) +{ + smartlist_t *conns = connection_dir_list_by_purpose_and_resource( + purpose, + resource); + CONN_LEN_AND_FREE_TEMPLATE(conns); +} + +/** Return a count of directory connections that are fetching the item + * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. */ +static inline int +connection_dir_count_by_purpose_resource_and_state( + int purpose, + const char *resource, + int state) +{ + smartlist_t *conns = + connection_dir_list_by_purpose_resource_and_state( + purpose, + resource, + state); + CONN_LEN_AND_FREE_TEMPLATE(conns); +} + +#undef CONN_LEN_AND_FREE_TEMPLATE int any_other_active_or_conns(const or_connection_t *this_conn); @@ -210,6 +253,10 @@ int connection_or_nonopen_was_started_here(or_connection_t *conn); void connection_dump_buffer_mem_stats(int severity); void remove_file_if_very_old(const char *fname, time_t now); +void clock_skew_warning(const connection_t *conn, long apparent_skew, + int trusted, log_domain_mask_t domain, + const char *received, const char *source); + #ifdef USE_BUFFEREVENTS int connection_type_uses_bufferevent(connection_t *conn); void connection_configure_bufferevent_callbacks(connection_t *conn); @@ -235,6 +282,13 @@ void connection_buckets_note_empty_ts(uint32_t *timestamp_var, int tokens_before, size_t tokens_removed, const struct timeval *tvnow); +MOCK_DECL(STATIC int,connection_connect_sockaddr, + (connection_t *conn, + const struct sockaddr *sa, + socklen_t sa_len, + const struct sockaddr *bindaddr, + socklen_t bindaddr_len, + int *socket_error)); #endif #endif diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 2a1a2f0fd2..754e9762ea 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -11,6 +11,9 @@ #define CONNECTION_EDGE_PRIVATE #include "or.h" + +#include "backtrace.h" + #include "addressmap.h" #include "buffers.h" #include "channel.h" @@ -102,8 +105,7 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason, * but we should fix it someday anyway. */ if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) && connection_edge_is_rendezvous_stream(edge_conn)) { - rend_client_note_connection_attempt_ended( - edge_conn->rend_data->onion_address); + rend_client_note_connection_attempt_ended(edge_conn->rend_data); } if (base_conn->marked_for_close) { @@ -504,6 +506,16 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) return connection_edge_process_inbuf(edge_conn, 1); } +/** A list of all the entry_connection_t * objects that are not marked + * for close, and are in AP_CONN_STATE_CIRCUIT_WAIT. + * + * (Right now, we check in several places to make sure that this list is + * correct. When it's incorrect, we'll fix it, and log a BUG message.) + */ +static smartlist_t *pending_entry_connections = NULL; + +static int untried_pending_connections = 0; + /** Common code to connection_(ap|exit)_about_to_close. */ static void connection_edge_about_to_close(edge_connection_t *edge_conn) @@ -526,6 +538,8 @@ connection_ap_about_to_close(entry_connection_t *entry_conn) edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(entry_conn); connection_t *conn = ENTRY_TO_CONN(entry_conn); + connection_edge_about_to_close(edge_conn); + if (entry_conn->socks_request->has_finished == 0) { /* since conn gets removed right after this function finishes, * there's no point trying to send back a reply at this point. */ @@ -544,6 +558,20 @@ connection_ap_about_to_close(entry_connection_t *entry_conn) conn->marked_for_close_file, conn->marked_for_close); dnsserv_reject_request(entry_conn); } + + if (TO_CONN(edge_conn)->state == AP_CONN_STATE_CIRCUIT_WAIT) { + smartlist_remove(pending_entry_connections, entry_conn); + } + +#if 1 + /* Check to make sure that this isn't in pending_entry_connections if it + * didn't actually belong there. */ + if (TO_CONN(edge_conn)->type == CONN_TYPE_AP) { + connection_ap_warn_and_unmark_if_pending_circ(entry_conn, + "about_to_close"); + } +#endif + control_event_stream_bandwidth(edge_conn); control_event_stream_status(entry_conn, STREAM_EVENT_CLOSED, edge_conn->end_reason); @@ -712,26 +740,181 @@ connection_ap_expire_beginning(void) } SMARTLIST_FOREACH_END(base_conn); } -/** Tell any AP streams that are waiting for a new circuit to try again, - * either attaching to an available circ or launching a new one. +/** + * As connection_ap_attach_pending, but first scans the entire connection + * array to see if any elements are missing. */ void -connection_ap_attach_pending(void) +connection_ap_rescan_and_attach_pending(void) { entry_connection_t *entry_conn; smartlist_t *conns = get_connection_array(); + + if (PREDICT_UNLIKELY(NULL == pending_entry_connections)) + pending_entry_connections = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { if (conn->marked_for_close || conn->type != CONN_TYPE_AP || conn->state != AP_CONN_STATE_CIRCUIT_WAIT) continue; + entry_conn = TO_ENTRY_CONN(conn); + tor_assert(entry_conn); + if (! smartlist_contains(pending_entry_connections, entry_conn)) { + log_warn(LD_BUG, "Found a connection %p that was supposed to be " + "in pending_entry_connections, but wasn't. No worries; " + "adding it.", + pending_entry_connections); + untried_pending_connections = 1; + connection_ap_mark_as_pending_circuit(entry_conn); + } + + } SMARTLIST_FOREACH_END(conn); + + connection_ap_attach_pending(1); +} + +#ifdef DEBUGGING_17659 +#define UNMARK() do { \ + entry_conn->marked_pending_circ_line = 0; \ + entry_conn->marked_pending_circ_file = 0; \ + } while (0) +#else +#define UNMARK() do { } while (0) +#endif + +/** Tell any AP streams that are listed as waiting for a new circuit to try + * again, either attaching to an available circ or launching a new one. + * + * If <b>retry</b> is false, only check the list if it contains at least one + * streams that we have not yet tried to attach to a circuit. + */ +void +connection_ap_attach_pending(int retry) +{ + if (PREDICT_UNLIKELY(!pending_entry_connections)) { + return; + } + + if (untried_pending_connections == 0 && !retry) + return; + + /* Don't allow modifications to pending_entry_connections while we are + * iterating over it. */ + smartlist_t *pending = pending_entry_connections; + pending_entry_connections = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(pending, + entry_connection_t *, entry_conn) { + connection_t *conn = ENTRY_TO_CONN(entry_conn); + tor_assert(conn && entry_conn); + if (conn->marked_for_close) { + UNMARK(); + continue; + } + if (conn->magic != ENTRY_CONNECTION_MAGIC) { + log_warn(LD_BUG, "%p has impossible magic value %u.", + entry_conn, (unsigned)conn->magic); + UNMARK(); + continue; + } + if (conn->state != AP_CONN_STATE_CIRCUIT_WAIT) { + log_warn(LD_BUG, "%p is no longer in circuit_wait. Its current state " + "is %s. Why is it on pending_entry_connections?", + entry_conn, + conn_state_to_string(conn->type, conn->state)); + UNMARK(); + continue; + } + if (connection_ap_handshake_attach_circuit(entry_conn) < 0) { if (!conn->marked_for_close) connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_CANT_ATTACH); } - } SMARTLIST_FOREACH_END(conn); + + if (! conn->marked_for_close && + conn->type == CONN_TYPE_AP && + conn->state == AP_CONN_STATE_CIRCUIT_WAIT) { + if (!smartlist_contains(pending_entry_connections, entry_conn)) { + smartlist_add(pending_entry_connections, entry_conn); + continue; + } + } + + UNMARK(); + } SMARTLIST_FOREACH_END(entry_conn); + + smartlist_free(pending); + untried_pending_connections = 0; +} + +/** Mark <b>entry_conn</b> as needing to get attached to a circuit. + * + * And <b>entry_conn</b> must be in AP_CONN_STATE_CIRCUIT_WAIT, + * should not already be pending a circuit. The circuit will get + * launched or the connection will get attached the next time we + * call connection_ap_attach_pending(). + */ +void +connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, + const char *fname, int lineno) +{ + connection_t *conn = ENTRY_TO_CONN(entry_conn); + tor_assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT); + tor_assert(conn->magic == ENTRY_CONNECTION_MAGIC); + if (conn->marked_for_close) + return; + + if (PREDICT_UNLIKELY(NULL == pending_entry_connections)) + pending_entry_connections = smartlist_new(); + + if (PREDICT_UNLIKELY(smartlist_contains(pending_entry_connections, + entry_conn))) { + log_warn(LD_BUG, "What?? pending_entry_connections already contains %p! " + "(Called from %s:%d.)", + entry_conn, fname, lineno); +#ifdef DEBUGGING_17659 + const char *f2 = entry_conn->marked_pending_circ_file; + log_warn(LD_BUG, "(Previously called from %s:%d.)\n", + f2 ? f2 : "<NULL>", + entry_conn->marked_pending_circ_line); +#endif + log_backtrace(LOG_WARN, LD_BUG, "To debug, this may help"); + return; + } + +#ifdef DEBUGGING_17659 + entry_conn->marked_pending_circ_line = (uint16_t) lineno; + entry_conn->marked_pending_circ_file = fname; +#endif + + untried_pending_connections = 1; + smartlist_add(pending_entry_connections, entry_conn); +} + +/** Mark <b>entry_conn</b> as no longer waiting for a circuit. */ +void +connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn) +{ + if (PREDICT_UNLIKELY(NULL == pending_entry_connections)) + return; + UNMARK(); + smartlist_remove(pending_entry_connections, entry_conn); +} + +/* DOCDOC */ +void +connection_ap_warn_and_unmark_if_pending_circ(entry_connection_t *entry_conn, + const char *where) +{ + if (pending_entry_connections && + smartlist_contains(pending_entry_connections, entry_conn)) { + log_warn(LD_BUG, "What was %p doing in pending_entry_connections in %s?", + entry_conn, where); + connection_ap_mark_as_non_pending_circuit(entry_conn); + } } /** Tell any AP streams that are waiting for a one-hop tunnel to @@ -852,12 +1035,13 @@ connection_ap_detach_retriable(entry_connection_t *conn, * a tunneled directory connection, then just attach it. */ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CIRCUIT_WAIT; circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn)); - return connection_ap_handshake_attach_circuit(conn); + connection_ap_mark_as_pending_circuit(conn); } else { + CONNECTION_AP_EXPECT_NONPENDING(conn); ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; circuit_detach_stream(TO_CIRCUIT(circ),ENTRY_TO_EDGE_CONN(conn)); - return 0; } + return 0; } /** Check if <b>conn</b> is using a dangerous port. Then warn and/or @@ -906,6 +1090,7 @@ connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn, const or_options_t *options = get_options(); if (options->LeaveStreamsUnattached) { + CONNECTION_AP_EXPECT_NONPENDING(conn); ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; return 0; } @@ -1455,10 +1640,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, /* If we were given a circuit to attach to, try to attach. Otherwise, * try to find a good one and attach to that. */ int rv; - if (circ) - rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath); - else - rv = connection_ap_handshake_attach_circuit(conn); + if (circ) { + rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath); + } else { + connection_ap_mark_as_pending_circuit(conn); + rv = 0; + } /* If the above function returned 0 then we're waiting for a circuit. * if it returned 1, we're attached. Both are okay. But if it returned @@ -1499,62 +1686,74 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, return -1; } + /* Look up if we have client authorization configured for this hidden + * service. If we do, associate it with the rend_data. */ + rend_service_authorization_t *client_auth = + rend_client_lookup_service_authorization(socks->address); + + const char *cookie = NULL; + rend_auth_type_t auth_type = REND_NO_AUTH; + if (client_auth) { + log_info(LD_REND, "Using previously configured client authorization " + "for hidden service request."); + auth_type = client_auth->auth_type; + cookie = client_auth->descriptor_cookie; + } + /* Fill in the rend_data field so we can start doing a connection to * a hidden service. */ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data = - tor_malloc_zero(sizeof(rend_data_t)); - strlcpy(rend_data->onion_address, socks->address, - sizeof(rend_data->onion_address)); + rend_data_client_create(socks->address, NULL, cookie, auth_type); + if (rend_data == NULL) { + return -1; + } log_info(LD_REND,"Got a hidden service request for ID '%s'", safe_str_client(rend_data->onion_address)); - /* see if we already have a hidden service descriptor cached for this - * address. */ + /* Lookup the given onion address. If invalid, stop right now else we + * might have it in the cache or not, it will be tested later on. */ + unsigned int refetch_desc = 0; rend_cache_entry_t *entry = NULL; const int rend_cache_lookup_result = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry); if (rend_cache_lookup_result < 0) { - /* We should already have rejected this address! */ - log_warn(LD_BUG,"Invalid service name '%s'", - safe_str_client(rend_data->onion_address)); - connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); - return -1; + switch (-rend_cache_lookup_result) { + case EINVAL: + /* We should already have rejected this address! */ + log_warn(LD_BUG,"Invalid service name '%s'", + safe_str_client(rend_data->onion_address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); + return -1; + case ENOENT: + refetch_desc = 1; + break; + default: + log_warn(LD_BUG, "Unknown cache lookup error %d", + rend_cache_lookup_result); + return -1; + } } /* Help predict this next time. We're not sure if it will need * a stable circuit yet, but we know we'll need *something*. */ rep_hist_note_used_internal(now, 0, 1); - /* Look up if we have client authorization configured for this hidden - * service. If we do, associate it with the rend_data. */ - rend_service_authorization_t *client_auth = - rend_client_lookup_service_authorization( - rend_data->onion_address); - if (client_auth) { - log_info(LD_REND, "Using previously configured client authorization " - "for hidden service request."); - memcpy(rend_data->descriptor_cookie, - client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN); - rend_data->auth_type = client_auth->auth_type; - } - - /* Now, we either launch an attempt to connect to the hidden service, - * or we launch an attempt to look up its descriptor, depending on - * whether we had the descriptor. */ - if (rend_cache_lookup_result == 0) { + /* Now we have a descriptor but is it usable or not? If not, refetch. + * Also, a fetch could have been requested if the onion address was not + * found in the cache previously. */ + if (refetch_desc || !rend_client_any_intro_points_usable(entry)) { + connection_ap_mark_as_non_pending_circuit(conn); base_conn->state = AP_CONN_STATE_RENDDESC_WAIT; log_info(LD_REND, "Unknown descriptor %s. Fetching.", - safe_str_client(rend_data->onion_address)); + safe_str_client(rend_data->onion_address)); rend_client_refetch_v2_renddesc(rend_data); - } else { /* rend_cache_lookup_result > 0 */ - base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; - log_info(LD_REND, "Descriptor is here. Great."); - if (connection_ap_handshake_attach_circuit(conn) < 0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return -1; - } + return 0; } + + /* We have the descriptor so launch a connection to the HS. */ + base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT; + log_info(LD_REND, "Descriptor is here. Great."); + connection_ap_mark_as_pending_circuit(conn); return 0; } @@ -2114,8 +2313,9 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) } log_info(LD_APP, - "Sending relay cell %d to begin stream %d.", + "Sending relay cell %d on circ %u to begin stream %d.", (int)ap_conn->use_begindir, + (unsigned)circ->base_.n_circ_id, edge_conn->stream_id); begin_type = ap_conn->use_begindir ? @@ -2309,12 +2509,7 @@ connection_ap_make_link(connection_t *partner, control_event_stream_status(conn, STREAM_EVENT_NEW, 0); /* attaching to a dirty circuit is fine */ - if (connection_ap_handshake_attach_circuit(conn) < 0) { - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - return NULL; - } - + connection_ap_mark_as_pending_circuit(conn); log_info(LD_APP,"... application connection created and linked."); return conn; } @@ -2756,8 +2951,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) return 0; } /* Make sure to get the 'real' address of the previous hop: the - * caller might want to know whether his IP address has changed, and - * we might already have corrected base_.addr[ess] for the relay's + * caller might want to know whether the remote IP address has changed, + * and we might already have corrected base_.addr[ess] for the relay's * canonical IP address. */ if (or_circ && or_circ->p_chan) address = tor_strdup(channel_get_actual_remote_address(or_circ->p_chan)); @@ -2846,6 +3041,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) origin_circ->p_streams = n_stream; assert_circuit_ok(circ); + origin_circ->rend_data->nr_streams++; + connection_exit_connect(n_stream); /* For path bias: This circuit was used successfully */ @@ -3461,3 +3658,12 @@ circuit_clear_isolation(origin_circuit_t *circ) circ->socks_username_len = circ->socks_password_len = 0; } +/** Free all storage held in module-scoped variables for connection_edge.c */ +void +connection_edge_free_all(void) +{ + untried_pending_connections = 0; + smartlist_free(pending_entry_connections); + pending_entry_connections = NULL; +} + diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 7c0b9c0767..5dfc8af901 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -64,7 +64,20 @@ int connection_edge_is_rendezvous_stream(edge_connection_t *conn); int connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit); void connection_ap_expire_beginning(void); -void connection_ap_attach_pending(void); +void connection_ap_rescan_and_attach_pending(void); +void connection_ap_attach_pending(int retry); +void connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, + const char *file, int line); +#define connection_ap_mark_as_pending_circuit(c) \ + connection_ap_mark_as_pending_circuit_((c), __FILE__, __LINE__) +void connection_ap_mark_as_non_pending_circuit(entry_connection_t *entry_conn); +#define CONNECTION_AP_EXPECT_NONPENDING(c) do { \ + if (ENTRY_TO_CONN(c)->state == AP_CONN_STATE_CIRCUIT_WAIT) { \ + log_warn(LD_BUG, "At %s:%d: %p was unexpectedly in circuit_wait.", \ + __FILE__, __LINE__, (c)); \ + connection_ap_mark_as_non_pending_circuit(c); \ + } \ + } while (0) void connection_ap_fail_onehop(const char *failed_digest, cpath_build_state_t *build_state); void circuit_discard_optional_exit_enclaves(extend_info_t *info); @@ -100,6 +113,12 @@ int connection_edge_update_circuit_isolation(const entry_connection_t *conn, void circuit_clear_isolation(origin_circuit_t *circ); streamid_t get_unique_stream_id_by_circ(origin_circuit_t *circ); +void connection_edge_free_all(void); + +void connection_ap_warn_and_unmark_if_pending_circ( + entry_connection_t *entry_conn, + const char *where); + /** @name Begin-cell flags * * These flags are used in RELAY_BEGIN cells to change the default behavior diff --git a/src/or/connection_or.c b/src/or/connection_or.c index e0dff1c915..9730e1a952 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -30,6 +30,8 @@ #include "entrynodes.h" #include "geoip.h" #include "main.h" +#include "link_handshake.h" +#include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" #include "reasons.h" @@ -487,6 +489,28 @@ var_cell_new(uint16_t payload_len) return cell; } +/** + * Copy a var_cell_t + */ + +var_cell_t * +var_cell_copy(const var_cell_t *src) +{ + var_cell_t *copy = NULL; + size_t size = 0; + + if (src != NULL) { + size = STRUCT_OFFSET(var_cell_t, payload) + src->payload_len; + copy = tor_malloc_zero(size); + copy->payload_len = src->payload_len; + copy->command = src->command; + copy->circ_id = src->circ_id; + memcpy(copy->payload, src->payload, copy->payload_len); + } + + return copy; +} + /** Release all space held by <b>cell</b>. */ void var_cell_free(var_cell_t *cell) @@ -1318,8 +1342,8 @@ connection_or_close_normally(or_connection_t *orconn, int flush) * the error state. */ -void -connection_or_close_for_error(or_connection_t *orconn, int flush) +MOCK_IMPL(void, +connection_or_close_for_error,(or_connection_t *orconn, int flush)) { channel_t *chan = NULL; @@ -1449,17 +1473,12 @@ connection_tls_continue_handshake(or_connection_t *conn) { int result; check_no_tls_errors(); - again: - if (conn->base_.state == OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING) { - // log_notice(LD_OR, "Renegotiate with %p", conn->tls); - result = tor_tls_renegotiate(conn->tls); - // log_notice(LD_OR, "Result: %d", result); - } else { - tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); - // log_notice(LD_OR, "Continue handshake with %p", conn->tls); - result = tor_tls_handshake(conn->tls); - // log_notice(LD_OR, "Result: %d", result); - } + + tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); + // log_notice(LD_OR, "Continue handshake with %p", conn->tls); + result = tor_tls_handshake(conn->tls); + // log_notice(LD_OR, "Result: %d", result); + switch (result) { CASE_TOR_TLS_ERROR_ANY: log_info(LD_OR,"tls error [%s]. breaking connection.", @@ -1468,23 +1487,10 @@ connection_tls_continue_handshake(or_connection_t *conn) case TOR_TLS_DONE: if (! tor_tls_used_v1_handshake(conn->tls)) { if (!tor_tls_is_server(conn->tls)) { - if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) { - if (tor_tls_received_v3_certificate(conn->tls)) { - log_info(LD_OR, "Client got a v3 cert! Moving on to v3 " - "handshake with ciphersuite %s", - tor_tls_get_ciphersuite_name(conn->tls)); - return connection_or_launch_v3_or_handshake(conn); - } else { - log_debug(LD_OR, "Done with initial SSL handshake (client-side)." - " Requesting renegotiation."); - connection_or_change_state(conn, - OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); - goto again; - } - } - // log_notice(LD_OR,"Done. state was %d.", conn->base_.state); + tor_assert(conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING); + return connection_or_launch_v3_or_handshake(conn); } else { - /* v2/v3 handshake, but not a client. */ + /* v2/v3 handshake, but we are not a client. */ log_debug(LD_OR, "Done with initial SSL handshake (server-side). " "Expecting renegotiation or VERSIONS cell"); tor_tls_set_renegotiate_callback(conn->tls, @@ -1497,6 +1503,7 @@ connection_tls_continue_handshake(or_connection_t *conn) return 0; } } + tor_assert(tor_tls_is_server(conn->tls)); return connection_tls_finish_handshake(conn); case TOR_TLS_WANTWRITE: connection_start_writing(TO_CONN(conn)); @@ -1532,22 +1539,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event, if (! tor_tls_used_v1_handshake(conn->tls)) { if (!tor_tls_is_server(conn->tls)) { if (conn->base_.state == OR_CONN_STATE_TLS_HANDSHAKING) { - if (tor_tls_received_v3_certificate(conn->tls)) { - log_info(LD_OR, "Client got a v3 cert!"); - if (connection_or_launch_v3_or_handshake(conn) < 0) - connection_or_close_for_error(conn, 0); - return; - } else { - connection_or_change_state(conn, - OR_CONN_STATE_TLS_CLIENT_RENEGOTIATING); - tor_tls_unblock_renegotiation(conn->tls); - if (bufferevent_ssl_renegotiate(conn->base_.bufev)<0) { - log_warn(LD_OR, "Start_renegotiating went badly."); - connection_or_close_for_error(conn, 0); - } - tor_tls_unblock_renegotiation(conn->tls); - return; /* ???? */ - } + if (connection_or_launch_v3_or_handshake(conn) < 0) + connection_or_close_for_error(conn, 0); } } else { const int handshakes = tor_tls_get_num_server_handshakes(conn->tls); @@ -1611,11 +1604,11 @@ connection_or_nonopen_was_started_here(or_connection_t *conn) } /** <b>Conn</b> just completed its handshake. Return 0 if all is well, and - * return -1 if he is lying, broken, or otherwise something is wrong. + * return -1 if they are lying, broken, or otherwise something is wrong. * * If we initiated this connection (<b>started_here</b> is true), make sure * the other side sent a correctly formed certificate. If I initiated the - * connection, make sure it's the right guy. + * connection, make sure it's the right relay by checking the certificate. * * Otherwise (if we _didn't_ initiate this connection), it's okay for * the certificate to be weird or absent. @@ -1631,7 +1624,7 @@ connection_or_nonopen_was_started_here(or_connection_t *conn) * 1) Set conn->circ_id_type according to tor-spec.txt. * 2) If we're an authdirserver and we initiated the connection: drop all * descriptors that claim to be on that IP/port but that aren't - * this guy; and note that this guy is reachable. + * this relay; and note that this relay is reachable. * 3) If this is a bridge and we didn't configure its identity * fingerprint, remember the keyid we just learned. */ @@ -1706,9 +1699,17 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, * or renegotiation. For v3 handshakes, this is right after we get a * certificate chain in a CERTS cell. * - * If we want any particular ID before, record the one we got. + * If we did not know the ID before, record the one we got. * - * If we wanted an ID, but we didn't get it, log a warning and return -1. + * If we wanted an ID, but we didn't get the one we expected, log a message + * and return -1. + * On relays: + * - log a protocol warning whenever the fingerprints don't match; + * On clients: + * - if a relay's fingerprint doesn't match, log a warning; + * - if we don't have updated relay fingerprints from a recent consensus, and + * a fallback directory mirror's hard-coded fingerprint has changed, log an + * info explaining that we will try another fallback. * * If we're testing reachability, remember what we learned. * @@ -1719,7 +1720,6 @@ connection_or_client_learned_peer_id(or_connection_t *conn, const uint8_t *peer_id) { const or_options_t *options = get_options(); - int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; if (tor_digest_is_zero(conn->identity_digest)) { connection_or_set_identity_digest(conn, (const char*)peer_id); @@ -1744,10 +1744,43 @@ connection_or_client_learned_peer_id(or_connection_t *conn, base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN); base16_encode(expected, sizeof(expected), conn->identity_digest, DIGEST_LEN); + const int using_hardcoded_fingerprints = + !networkstatus_get_reasonably_live_consensus(time(NULL), + usable_consensus_flavor()); + const int is_fallback_fingerprint = router_digest_is_fallback_dir( + conn->identity_digest); + const int is_authority_fingerprint = router_digest_is_trusted_dir( + conn->identity_digest); + int severity; + const char *extra_log = ""; + + if (server_mode(options)) { + severity = LOG_PROTOCOL_WARN; + } else { + if (using_hardcoded_fingerprints) { + /* We need to do the checks in this order, because the list of + * fallbacks includes the list of authorities */ + if (is_authority_fingerprint) { + severity = LOG_WARN; + } else if (is_fallback_fingerprint) { + /* we expect a small number of fallbacks to change from their + * hard-coded fingerprints over the life of a release */ + severity = LOG_INFO; + extra_log = " Tor will try a different fallback."; + } else { + /* it's a bridge, it's either a misconfiguration, or unexpected */ + severity = LOG_WARN; + } + } else { + /* a relay has changed its fingerprint from the one in the consensus */ + severity = LOG_WARN; + } + } + log_fn(severity, LD_HANDSHAKE, "Tried connecting to router at %s:%d, but identity key was not " - "as expected: wanted %s but got %s.", - conn->base_.address, conn->base_.port, expected, seen); + "as expected: wanted %s but got %s.%s", + conn->base_.address, conn->base_.port, expected, seen, extra_log); entry_guard_register_connect_status(conn->identity_digest, 0, 1, time(NULL)); control_event_or_conn_status(conn, OR_CONN_EVENT_FAILED, @@ -1784,7 +1817,7 @@ connection_or_client_used(or_connection_t *conn) * * Make sure we are happy with the person we just handshaked with. * - * If he initiated the connection, make sure he's not already connected, + * If they initiated the connection, make sure they're not already connected, * then initialize conn from the information in router. * * If all is successful, call circuit_n_conn_done() to handle events @@ -1799,6 +1832,8 @@ connection_tls_finish_handshake(or_connection_t *conn) char digest_rcvd[DIGEST_LEN]; int started_here = connection_or_nonopen_was_started_here(conn); + tor_assert(!started_here); + log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using " "ciphersuite %s. verifying.", started_here?"outgoing":"incoming", @@ -1814,10 +1849,8 @@ connection_tls_finish_handshake(or_connection_t *conn) if (tor_tls_used_v1_handshake(conn->tls)) { conn->link_proto = 1; - if (!started_here) { - connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); - } + connection_or_init_conn_from_address(conn, &conn->base_.addr, + conn->base_.port, digest_rcvd, 0); tor_tls_block_renegotiation(conn->tls); rep_hist_note_negotiated_link_proto(1, started_here); return connection_or_set_state_open(conn); @@ -1825,10 +1858,8 @@ connection_tls_finish_handshake(or_connection_t *conn) connection_or_change_state(conn, OR_CONN_STATE_OR_HANDSHAKING_V2); if (connection_init_or_handshake_state(conn, started_here) < 0) return -1; - if (!started_here) { - connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); - } + connection_or_init_conn_from_address(conn, &conn->base_.addr, + conn->base_.port, digest_rcvd, 0); return connection_or_send_versions(conn, 0); } } @@ -1843,7 +1874,6 @@ static int connection_or_launch_v3_or_handshake(or_connection_t *conn) { tor_assert(connection_or_nonopen_was_started_here(conn)); - tor_assert(tor_tls_received_v3_certificate(conn->tls)); circuit_build_times_network_is_live(get_circuit_build_times_mutable()); @@ -1879,8 +1909,8 @@ or_handshake_state_free(or_handshake_state_t *state) return; crypto_digest_free(state->digest_sent); crypto_digest_free(state->digest_received); - tor_cert_free(state->auth_cert); - tor_cert_free(state->id_cert); + tor_x509_cert_free(state->auth_cert); + tor_x509_cert_free(state->id_cert); memwipe(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } @@ -2013,9 +2043,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) * <b>conn</b>'s outbuf. Right now, this <em>DOES NOT</em> support cells that * affect a circuit. */ -void -connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn) +MOCK_IMPL(void, +connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)) { int n; char hdr[VAR_CELL_MAX_HEADER_SIZE]; @@ -2059,6 +2089,19 @@ connection_or_process_cells_from_inbuf(or_connection_t *conn) { var_cell_t *var_cell; + /* + * Note on memory management for incoming cells: below the channel layer, + * we shouldn't need to consider its internal queueing/copying logic. It + * is safe to pass cells to it on the stack or on the heap, but in the + * latter case we must be sure we free them later. + * + * The incoming cell queue code in channel.c will (in the common case) + * decide it can pass them to the upper layer immediately, in which case + * those functions may run directly on the cell pointers we pass here, or + * it may decide to queue them, in which case it will allocate its own + * buffer and copy the cell. + */ + while (1) { log_debug(LD_OR, TOR_SOCKET_T_FORMAT": starting, inbuf_datalen %d " @@ -2158,8 +2201,8 @@ connection_or_send_versions(or_connection_t *conn, int v3_plus) /** Send a NETINFO cell on <b>conn</b>, telling the other server what we know * about their address, our address, and the current time. */ -int -connection_or_send_netinfo(or_connection_t *conn) +MOCK_IMPL(int, +connection_or_send_netinfo,(or_connection_t *conn)) { cell_t cell; time_t now = time(NULL); @@ -2228,7 +2271,7 @@ connection_or_send_netinfo(or_connection_t *conn) int connection_or_send_certs_cell(or_connection_t *conn) { - const tor_cert_t *link_cert = NULL, *id_cert = NULL; + const tor_x509_cert_t *link_cert = NULL, *id_cert = NULL; const uint8_t *link_encoded = NULL, *id_encoded = NULL; size_t link_len, id_len; var_cell_t *cell; @@ -2243,8 +2286,8 @@ connection_or_send_certs_cell(or_connection_t *conn) server_mode = ! conn->handshake_state->started_here; if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0) return -1; - tor_cert_get_der(link_cert, &link_encoded, &link_len); - tor_cert_get_der(id_cert, &id_encoded, &id_len); + tor_x509_cert_get_der(link_cert, &link_encoded, &link_len); + tor_x509_cert_get_der(id_cert, &id_encoded, &id_len); cell_len = 1 /* 1 byte: num certs in cell */ + 2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ + @@ -2280,28 +2323,36 @@ connection_or_send_certs_cell(or_connection_t *conn) int connection_or_send_auth_challenge_cell(or_connection_t *conn) { - var_cell_t *cell; - uint8_t *cp; - uint8_t challenge[OR_AUTH_CHALLENGE_LEN]; + var_cell_t *cell = NULL; + int r = -1; tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; - if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0) - return -1; - cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4); + auth_challenge_cell_t *ac = auth_challenge_cell_new(); + + crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); + + auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + auth_challenge_cell_set_n_methods(ac, + auth_challenge_cell_getlen_methods(ac)); + + cell = var_cell_new(auth_challenge_cell_encoded_len(ac)); + ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len, + ac); + if (len != cell->payload_len) + goto done; cell->command = CELL_AUTH_CHALLENGE; - memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN); - cp = cell->payload + OR_AUTH_CHALLENGE_LEN; - set_uint16(cp, htons(1)); /* We recognize one authentication type. */ - set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); connection_or_write_var_cell_to_buf(cell, conn); + r = 0; + + done: var_cell_free(cell); - memwipe(challenge, 0, sizeof(challenge)); + auth_challenge_cell_free(ac); - return 0; + return r; } /** Compute the main body of an AUTHENTICATE cell that a client can use @@ -2328,28 +2379,28 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, crypto_pk_t *signing_key, int server) { - uint8_t *ptr; + auth1_t *auth = NULL; + auth_ctx_t *ctx = auth_ctx_new(); + int result; /* assert state is reasonable XXXX */ - if (outlen < V3_AUTH_FIXED_PART_LEN || - (!server && outlen < V3_AUTH_BODY_LEN)) - return -1; + ctx->is_ed = 0; - ptr = out; + auth = auth1_new(); /* Type: 8 bytes. */ - memcpy(ptr, "AUTH0001", 8); - ptr += 8; + memcpy(auth1_getarray_type(auth), "AUTH0001", 8); { - const tor_cert_t *id_cert=NULL, *link_cert=NULL; - const digests_t *my_digests, *their_digests; + const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL; + const common_digests_t *my_digests, *their_digests; const uint8_t *my_id, *their_id, *client_id, *server_id; if (tor_tls_get_my_certs(server, &link_cert, &id_cert)) - return -1; - my_digests = tor_cert_get_id_digests(id_cert); - their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); + goto err; + my_digests = tor_x509_cert_get_id_digests(id_cert); + their_digests = + tor_x509_cert_get_id_digests(conn->handshake_state->id_cert); tor_assert(my_digests); tor_assert(their_digests); my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; @@ -2359,12 +2410,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, server_id = server ? my_id : their_id; /* Client ID digest: 32 octets. */ - memcpy(ptr, client_id, 32); - ptr += 32; + memcpy(auth->cid, client_id, 32); /* Server ID digest: 32 octets. */ - memcpy(ptr, server_id, 32); - ptr += 32; + memcpy(auth->sid, server_id, 32); } { @@ -2378,73 +2427,101 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } /* Server log digest : 32 octets */ - crypto_digest_get_digest(server_d, (char*)ptr, 32); - ptr += 32; + crypto_digest_get_digest(server_d, (char*)auth->slog, 32); /* Client log digest : 32 octets */ - crypto_digest_get_digest(client_d, (char*)ptr, 32); - ptr += 32; + crypto_digest_get_digest(client_d, (char*)auth->clog, 32); } { /* Digest of cert used on TLS link : 32 octets. */ - const tor_cert_t *cert = NULL; - tor_cert_t *freecert = NULL; + const tor_x509_cert_t *cert = NULL; + tor_x509_cert_t *freecert = NULL; if (server) { tor_tls_get_my_certs(1, &cert, NULL); } else { freecert = tor_tls_get_peer_cert(conn->tls); cert = freecert; } - if (!cert) - return -1; - memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + if (!cert) { + log_warn(LD_OR, "Unable to find cert when making AUTH1 data."); + goto err; + } + + memcpy(auth->scert, + tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); if (freecert) - tor_cert_free(freecert); - ptr += 32; + tor_x509_cert_free(freecert); } /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - tor_tls_get_tlssecrets(conn->tls, ptr); - ptr += 32; - - tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN); - - if (server) - return V3_AUTH_FIXED_PART_LEN; // ptr-out + tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); /* 8 octets were reserved for the current time, but we're trying to get out * of the habit of sending time around willynilly. Fortunately, nothing * checks it. That's followed by 16 bytes of nonce. */ - crypto_rand((char*)ptr, 24); - ptr += 24; + crypto_rand((char*)auth->rand, 24); - tor_assert(ptr - out == V3_AUTH_BODY_LEN); + ssize_t len; + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { + log_warn(LD_OR, "Unable to encode signed part of AUTH1 data."); + goto err; + } - if (!signing_key) - return V3_AUTH_BODY_LEN; // ptr - out + if (server) { + auth1_t *tmp = NULL; + ssize_t len2 = auth1_parse(&tmp, out, len, ctx); + if (!tmp) { + log_warn(LD_OR, "Unable to parse signed part of AUTH1 data."); + goto err; + } + result = (int) (tmp->end_of_fixed_part - out); + auth1_free(tmp); + if (len2 != len) { + log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data."); + goto err; + } + goto done; + } + + if (signing_key) { + auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); - { - int siglen; char d[32]; - crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256); - siglen = crypto_pk_private_sign(signing_key, - (char*)ptr, outlen - (ptr-out), + crypto_digest256(d, (char*)out, len, DIGEST_SHA256); + int siglen = crypto_pk_private_sign(signing_key, + (char*)auth1_getarray_sig(auth), + auth1_getlen_sig(auth), d, 32); - if (siglen < 0) - return -1; + if (siglen < 0) { + log_warn(LD_OR, "Unable to sign AUTH1 data."); + goto err; + } + + auth1_setlen_sig(auth, siglen); - ptr += siglen; - tor_assert(ptr <= out+outlen); - return (int)(ptr - out); + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + log_warn(LD_OR, "Unable to encode signed AUTH1 data."); + goto err; + } } + result = (int) len; + goto done; + + err: + result = -1; + done: + auth1_free(auth); + auth_ctx_free(ctx); + return result; } /** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on * success, -1 on failure */ -int -connection_or_send_authenticate_cell(or_connection_t *conn, int authtype) +MOCK_IMPL(int, +connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype)) { var_cell_t *cell; crypto_pk_t *pk = tor_tls_get_my_client_auth_key(); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index fc261c6bac..e2ec47a4f2 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -43,7 +43,8 @@ MOCK_DECL(or_connection_t *, const char *id_digest, channel_tls_t *chan)); void connection_or_close_normally(or_connection_t *orconn, int flush); -void connection_or_close_for_error(or_connection_t *orconn, int flush); +MOCK_DECL(void,connection_or_close_for_error, + (or_connection_t *orconn, int flush)); void connection_or_report_broken_states(int severity, int domain); @@ -77,17 +78,18 @@ void or_handshake_state_record_var_cell(or_connection_t *conn, int connection_or_set_state_open(or_connection_t *conn); void connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn); -void connection_or_write_var_cell_to_buf(const var_cell_t *cell, - or_connection_t *conn); +MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell, + or_connection_t *conn)); int connection_or_send_versions(or_connection_t *conn, int v3_plus); -int connection_or_send_netinfo(or_connection_t *conn); +MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn)); int connection_or_send_certs_cell(or_connection_t *conn); int connection_or_send_auth_challenge_cell(or_connection_t *conn); int connection_or_compute_authenticate_cell_body(or_connection_t *conn, uint8_t *out, size_t outlen, crypto_pk_t *signing_key, int server); -int connection_or_send_authenticate_cell(or_connection_t *conn, int type); +MOCK_DECL(int,connection_or_send_authenticate_cell, + (or_connection_t *conn, int type)); int is_or_protocol_version_known(uint16_t version); @@ -95,9 +97,10 @@ void cell_pack(packed_cell_t *dest, const cell_t *src, int wide_circ_ids); int var_cell_pack_header(const var_cell_t *cell, char *hdr_out, int wide_circ_ids); var_cell_t *var_cell_new(uint16_t payload_len); +var_cell_t *var_cell_copy(const var_cell_t *src); void var_cell_free(var_cell_t *cell); -/** DOCDOC */ +/* DOCDOC */ #define MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS 4 #endif diff --git a/src/or/control.c b/src/or/control.c index e25c3b2954..e2ad8cc6dc 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -20,6 +20,7 @@ #include "circuitstats.h" #include "circuituse.h" #include "command.h" +#include "compat_libevent.h" #include "config.h" #include "confparse.h" #include "connection.h" @@ -37,6 +38,9 @@ #include "nodelist.h" #include "policies.h" #include "reasons.h" +#include "rendclient.h" +#include "rendcommon.h" +#include "rendservice.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -47,6 +51,12 @@ #include <sys/resource.h> #endif +#ifdef HAVE_EVENT2_EVENT_H +#include <event2/event.h> +#else +#include <event.h> +#endif + #include "crypto_s2k.h" #include "procmon.h" @@ -72,7 +82,7 @@ static int disable_log_messages = 0; /** Macro: true if any control connection is interested in events of type * <b>e</b>. */ #define EVENT_IS_INTERESTING(e) \ - (!! (global_event_mask & (((uint64_t)1)<<(e)))) + (!! (global_event_mask & EVENT_MASK_(e))) /** If we're using cookie-type authentication, how long should our cookies be? */ @@ -92,6 +102,11 @@ static uint8_t *authentication_cookie = NULL; "Tor safe cookie authentication controller-to-server hash" #define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN +/** The list of onion services that have been added via ADD_ONION that do not + * belong to any particular control connection. + */ +static smartlist_t *detached_onion_services = NULL; + /** A sufficiently large size to record the last bootstrap phase string. */ #define BOOTSTRAP_MSG_LEN 1024 @@ -102,17 +117,17 @@ static char last_sent_bootstrap_message[BOOTSTRAP_MSG_LEN]; static void connection_printf_to_buf(control_connection_t *conn, const char *format, ...) CHECK_PRINTF(2,3); -static void send_control_event_impl(uint16_t event, event_format_t which, +static void send_control_event_impl(uint16_t event, const char *format, va_list ap) - CHECK_PRINTF(3,0); + CHECK_PRINTF(2,0); static int control_event_status(int type, int severity, const char *format, va_list args) CHECK_PRINTF(3,0); static void send_control_done(control_connection_t *conn); -static void send_control_event(uint16_t event, event_format_t which, +static void send_control_event(uint16_t event, const char *format, ...) - CHECK_PRINTF(3,4); + CHECK_PRINTF(2,3); static int handle_control_setconf(control_connection_t *conn, uint32_t len, char *body); static int handle_control_resetconf(control_connection_t *conn, uint32_t len, @@ -157,14 +172,27 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len, static int handle_control_usefeature(control_connection_t *conn, uint32_t len, const char *body); +static int handle_control_hsfetch(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_hspost(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_add_onion(control_connection_t *conn, uint32_t len, + const char *body); +static int handle_control_del_onion(control_connection_t *conn, uint32_t len, + const char *body); static int write_stream_target_to_buf(entry_connection_t *conn, char *buf, size_t len); static void orconn_target_get_name(char *buf, size_t len, or_connection_t *conn); +static int get_cached_network_liveness(void); +static void set_cached_network_liveness(int liveness); + +static void flush_queued_events_cb(evutil_socket_t fd, short what, void *arg); + /** Given a control event code for a message event, return the corresponding * log severity. */ -static INLINE int +static inline int event_to_log_severity(int event) { switch (event) { @@ -178,7 +206,7 @@ event_to_log_severity(int event) } /** Given a log severity, return the corresponding control event code. */ -static INLINE int +static inline int log_severity_to_event(int severity) { switch (severity) { @@ -297,7 +325,7 @@ control_event_is_interesting(int event) /** Append a NUL-terminated string <b>s</b> to the end of * <b>conn</b>-\>outbuf. */ -static INLINE void +static inline void connection_write_str_to_buf(const char *s, control_connection_t *conn) { size_t len = strlen(s); @@ -400,7 +428,7 @@ read_escaped_data(const char *data, size_t len, char **out) /** If the first <b>in_len_max</b> characters in <b>start</b> contain a * double-quoted string with escaped characters, return the length of that * string (as encoded, including quotes). Otherwise return -1. */ -static INLINE int +static inline int get_escaped_string_length(const char *start, size_t in_len_max, int *chars_out) { @@ -559,46 +587,217 @@ send_control_done(control_connection_t *conn) connection_write_str_to_buf("250 OK\r\n", conn); } -/** Send an event to all v1 controllers that are listening for code - * <b>event</b>. The event's body is given by <b>msg</b>. +/** Represents an event that's queued to be sent to one or more + * controllers. */ +typedef struct queued_event_s { + uint16_t event; + char *msg; +} queued_event_t; + +/** Pointer to int. If this is greater than 0, we don't allow new events to be + * queued. */ +static tor_threadlocal_t block_event_queue; + +/** Holds a smartlist of queued_event_t objects that may need to be sent + * to one or more controllers */ +static smartlist_t *queued_control_events = NULL; + +/** True if the flush_queued_events_event is pending. */ +static int flush_queued_event_pending = 0; + +/** Lock to protect the above fields. */ +static tor_mutex_t *queued_control_events_lock = NULL; + +/** An event that should fire in order to flush the contents of + * queued_control_events. */ +static struct event *flush_queued_events_event = NULL; + +void +control_initialize_event_queue(void) +{ + if (queued_control_events == NULL) { + queued_control_events = smartlist_new(); + } + + if (flush_queued_events_event == NULL) { + struct event_base *b = tor_libevent_get_base(); + if (b) { + flush_queued_events_event = tor_event_new(b, + -1, 0, flush_queued_events_cb, + NULL); + tor_assert(flush_queued_events_event); + } + } + + if (queued_control_events_lock == NULL) { + queued_control_events_lock = tor_mutex_new(); + tor_threadlocal_init(&block_event_queue); + } +} + +static int * +get_block_event_queue(void) +{ + int *val = tor_threadlocal_get(&block_event_queue); + if (PREDICT_UNLIKELY(val == NULL)) { + val = tor_malloc_zero(sizeof(int)); + tor_threadlocal_set(&block_event_queue, val); + } + return val; +} + +/** Helper: inserts an event on the list of events queued to be sent to + * one or more controllers, and schedules the events to be flushed if needed. * - * If <b>which</b> & SHORT_NAMES, the event contains short-format names: send - * it to controllers that haven't enabled the VERBOSE_NAMES feature. If - * <b>which</b> & LONG_NAMES, the event contains long-format names: send it - * to controllers that <em>have</em> enabled VERBOSE_NAMES. + * This function takes ownership of <b>msg</b>, and may free it. * - * The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with - * respect to the EXTENDED_EVENTS feature. */ + * We queue these events rather than send them immediately in order to break + * the dependency in our callgraph from code that generates events for the + * controller, and the network layer at large. Otherwise, nearly every + * interesting part of Tor would potentially call every other interesting part + * of Tor. + */ MOCK_IMPL(STATIC void, -send_control_event_string,(uint16_t event, event_format_t which, - const char *msg)) +queue_control_event_string,(uint16_t event, char *msg)) { - smartlist_t *conns = get_connection_array(); - (void)which; - tor_assert(event >= EVENT_MIN_ && event <= EVENT_MAX_); + /* This is redundant with checks done elsewhere, but it's a last-ditch + * attempt to avoid queueing something we shouldn't have to queue. */ + if (PREDICT_UNLIKELY( ! EVENT_IS_INTERESTING(event) )) { + tor_free(msg); + return; + } - SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) { + int *block_event_queue = get_block_event_queue(); + if (*block_event_queue) { + tor_free(msg); + return; + } + + queued_event_t *ev = tor_malloc(sizeof(*ev)); + ev->event = event; + ev->msg = msg; + + /* No queueing an event while queueing an event */ + ++*block_event_queue; + + tor_mutex_acquire(queued_control_events_lock); + tor_assert(queued_control_events); + smartlist_add(queued_control_events, ev); + + int activate_event = 0; + if (! flush_queued_event_pending && in_main_thread()) { + activate_event = 1; + flush_queued_event_pending = 1; + } + + tor_mutex_release(queued_control_events_lock); + + --*block_event_queue; + + /* We just put an event on the queue; mark the queue to be + * flushed. We only do this from the main thread for now; otherwise, + * we'd need to incur locking overhead in Libevent or use a socket. + */ + if (activate_event) { + tor_assert(flush_queued_events_event); + event_active(flush_queued_events_event, EV_READ, 1); + } +} + +/** Release all storage held by <b>ev</b>. */ +static void +queued_event_free(queued_event_t *ev) +{ + if (ev == NULL) + return; + + tor_free(ev->msg); + tor_free(ev); +} + +/** Send every queued event to every controller that's interested in it, + * and remove the events from the queue. If <b>force</b> is true, + * then make all controllers send their data out immediately, since we + * may be about to shut down. */ +static void +queued_events_flush_all(int force) +{ + if (PREDICT_UNLIKELY(queued_control_events == NULL)) { + return; + } + smartlist_t *all_conns = get_connection_array(); + smartlist_t *controllers = smartlist_new(); + smartlist_t *queued_events; + + int *block_event_queue = get_block_event_queue(); + ++*block_event_queue; + + tor_mutex_acquire(queued_control_events_lock); + /* No queueing an event while flushing events. */ + flush_queued_event_pending = 0; + queued_events = queued_control_events; + queued_control_events = smartlist_new(); + tor_mutex_release(queued_control_events_lock); + + /* Gather all the controllers that will care... */ + SMARTLIST_FOREACH_BEGIN(all_conns, connection_t *, conn) { if (conn->type == CONN_TYPE_CONTROL && !conn->marked_for_close && conn->state == CONTROL_CONN_STATE_OPEN) { control_connection_t *control_conn = TO_CONTROL_CONN(conn); - if (control_conn->event_mask & (((event_mask_t)1)<<event)) { - int is_err = 0; - connection_write_to_buf(msg, strlen(msg), TO_CONN(control_conn)); - if (event == EVENT_ERR_MSG) - is_err = 1; - else if (event == EVENT_STATUS_GENERAL) - is_err = !strcmpstart(msg, "STATUS_GENERAL ERR "); - else if (event == EVENT_STATUS_CLIENT) - is_err = !strcmpstart(msg, "STATUS_CLIENT ERR "); - else if (event == EVENT_STATUS_SERVER) - is_err = !strcmpstart(msg, "STATUS_SERVER ERR "); - if (is_err) - connection_flush(TO_CONN(control_conn)); - } + smartlist_add(controllers, control_conn); } } SMARTLIST_FOREACH_END(conn); + + SMARTLIST_FOREACH_BEGIN(queued_events, queued_event_t *, ev) { + const event_mask_t bit = ((event_mask_t)1) << ev->event; + const size_t msg_len = strlen(ev->msg); + SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *, + control_conn) { + if (control_conn->event_mask & bit) { + connection_write_to_buf(ev->msg, msg_len, TO_CONN(control_conn)); + } + } SMARTLIST_FOREACH_END(control_conn); + + queued_event_free(ev); + } SMARTLIST_FOREACH_END(ev); + + if (force) { + SMARTLIST_FOREACH_BEGIN(controllers, control_connection_t *, + control_conn) { + connection_flush(TO_CONN(control_conn)); + } SMARTLIST_FOREACH_END(control_conn); + } + + smartlist_free(queued_events); + smartlist_free(controllers); + + --*block_event_queue; +} + +/** Libevent callback: Flushes pending events to controllers that are + * interested in them */ +static void +flush_queued_events_cb(evutil_socket_t fd, short what, void *arg) +{ + (void) fd; + (void) what; + (void) arg; + queued_events_flush_all(0); +} + +/** Send an event to all v1 controllers that are listening for code + * <b>event</b>. The event's body is given by <b>msg</b>. + * + * The EXTENDED_FORMAT and NONEXTENDED_FORMAT flags behave similarly with + * respect to the EXTENDED_EVENTS feature. */ +MOCK_IMPL(STATIC void, +send_control_event_string,(uint16_t event, + const char *msg)) +{ + tor_assert(event >= EVENT_MIN_ && event <= EVENT_MAX_); + queue_control_event_string(event, tor_strdup(msg)); } /** Helper for send_control_event and control_event_status: @@ -606,8 +805,8 @@ send_control_event_string,(uint16_t event, event_format_t which, * <b>event</b>. The event's body is created by the printf-style format in * <b>format</b>, and other arguments as provided. */ static void -send_control_event_impl(uint16_t event, event_format_t which, - const char *format, va_list ap) +send_control_event_impl(uint16_t event, + const char *format, va_list ap) { char *buf = NULL; int len; @@ -618,21 +817,19 @@ send_control_event_impl(uint16_t event, event_format_t which, return; } - send_control_event_string(event, which|ALL_FORMATS, buf); - - tor_free(buf); + queue_control_event_string(event, buf); } /** Send an event to all v1 controllers that are listening for code * <b>event</b>. The event's body is created by the printf-style format in * <b>format</b>, and other arguments as provided. */ static void -send_control_event(uint16_t event, event_format_t which, +send_control_event(uint16_t event, const char *format, ...) { va_list ap; va_start(ap, format); - send_control_event_impl(event, which, format, ap); + send_control_event_impl(event, format, ap); va_end(ap); } @@ -933,7 +1130,7 @@ static const struct control_event_t control_event_table[] = { { EVENT_CLIENTS_SEEN, "CLIENTS_SEEN" }, { EVENT_NEWCONSENSUS, "NEWCONSENSUS" }, { EVENT_BUILDTIMEOUT_SET, "BUILDTIMEOUT_SET" }, - { EVENT_SIGNAL, "SIGNAL" }, + { EVENT_GOT_SIGNAL, "SIGNAL" }, { EVENT_CONF_CHANGED, "CONF_CHANGED"}, { EVENT_CONN_BW, "CONN_BW" }, { EVENT_CELL_STATS, "CELL_STATS" }, @@ -941,6 +1138,8 @@ static const struct control_event_t control_event_table[] = { { EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" }, { EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" }, { EVENT_HS_DESC, "HS_DESC" }, + { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" }, + { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" }, { 0, NULL }, }; @@ -1304,7 +1503,7 @@ handle_control_signal(control_connection_t *conn, uint32_t len, if (sig == SIGTERM || sig == SIGINT) connection_flush(TO_CONN(conn)); - process_signal(sig); + activate_signal(sig); return 0; } @@ -1520,8 +1719,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, } #endif } else if (!strcmp(question, "process/descriptor-limit")) { - int max_fds=-1; - set_max_file_descriptors(0, &max_fds); + int max_fds = get_max_sockets(); tor_asprintf(answer, "%d", max_fds); } else if (!strcmp(question, "limits/max-mem-in-queues")) { tor_asprintf(answer, U64_FORMAT, @@ -1713,6 +1911,38 @@ getinfo_helper_dir(control_connection_t *control_conn, *answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); + } else if (!strcmpstart(question, "hs/client/desc/id/")) { + rend_cache_entry_t *e = NULL; + + question += strlen("hs/client/desc/id/"); + if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + *errmsg = "Invalid address"; + return -1; + } + + if (!rend_cache_lookup_entry(question, -1, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } + } else if (!strcmpstart(question, "hs/service/desc/id/")) { + rend_cache_entry_t *e = NULL; + + question += strlen("hs/service/desc/id/"); + if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) { + *errmsg = "Invalid address"; + return -1; + } + + if (!rend_cache_lookup_v2_desc_as_service(question, &e)) { + /* Descriptor found in cache */ + *answer = tor_strdup(e->desc); + } else { + *errmsg = "Not found in cache"; + return -1; + } } else if (!strcmpstart(question, "md/id/")) { const node_t *node = node_get_by_hex_id(question+strlen("md/id/")); const microdesc_t *md = NULL; @@ -1781,6 +2011,11 @@ getinfo_helper_dir(control_connection_t *control_conn, char *filename = get_datadir_fname("cached-consensus"); *answer = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); tor_free(filename); + if (!*answer) { /* generate an error */ + *errmsg = "Could not open cached consensus. " + "Make sure FetchUselessDescriptors is set to 1."; + return -1; + } } } else if (!strcmp(question, "network-status")) { /* v1 */ routerlist_t *routerlist = router_get_routerlist(); @@ -1913,6 +2148,7 @@ getinfo_helper_events(control_connection_t *control_conn, const char *question, char **answer, const char **errmsg) { + const or_options_t *options = get_options(); (void) control_conn; if (!strcmp(question, "circuit-status")) { smartlist_t *status = smartlist_new(); @@ -2049,17 +2285,19 @@ getinfo_helper_events(control_connection_t *control_conn, *answer = tor_strdup(directories_have_accepted_server_descriptor() ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/or")) { - *answer = tor_strdup(check_whether_orport_reachable() ? "1" : "0"); + *answer = tor_strdup(check_whether_orport_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/dir")) { - *answer = tor_strdup(check_whether_dirport_reachable() ? "1" : "0"); + *answer = tor_strdup(check_whether_dirport_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded")) { tor_asprintf(answer, "OR=%d DIR=%d", - check_whether_orport_reachable() ? 1 : 0, - check_whether_dirport_reachable() ? 1 : 0); + check_whether_orport_reachable(options) ? 1 : 0, + check_whether_dirport_reachable(options) ? 1 : 0); } else if (!strcmp(question, "status/bootstrap-phase")) { *answer = tor_strdup(last_sent_bootstrap_message); } else if (!strcmpstart(question, "status/version/")) { - int is_server = server_mode(get_options()); + int is_server = server_mode(options); networkstatus_t *c = networkstatus_get_latest_consensus(); version_status_t status; const char *recommended; @@ -2100,6 +2338,46 @@ getinfo_helper_events(control_connection_t *control_conn, return -1; } *answer = bridge_stats; + } else if (!strcmp(question, "status/fresh-relay-descs")) { + if (!server_mode(options)) { + *errmsg = "Only relays have descriptors"; + return -1; + } + routerinfo_t *r; + extrainfo_t *e; + if (router_build_fresh_descriptor(&r, &e) < 0) { + *errmsg = "Error generating descriptor"; + return -1; + } + size_t size = r->cache_info.signed_descriptor_len + 1; + if (e) { + size += e->cache_info.signed_descriptor_len + 1; + } + tor_assert(r->cache_info.signed_descriptor_len); + char *descs = tor_malloc(size); + char *cp = descs; + memcpy(cp, signed_descriptor_get_body(&r->cache_info), + r->cache_info.signed_descriptor_len); + cp += r->cache_info.signed_descriptor_len - 1; + if (e) { + if (cp[0] == '\0') { + cp[0] = '\n'; + } else if (cp[0] != '\n') { + cp[1] = '\n'; + cp++; + } + memcpy(cp, signed_descriptor_get_body(&e->cache_info), + e->cache_info.signed_descriptor_len); + cp += e->cache_info.signed_descriptor_len - 1; + } + if (cp[0] == '\n') { + cp[0] = '\0'; + } else if (cp[0] != '\0') { + cp[1] = '\0'; + } + *answer = descs; + routerinfo_free(r); + extrainfo_free(e); } else { return 0; } @@ -2107,6 +2385,55 @@ getinfo_helper_events(control_connection_t *control_conn, return 0; } +/** Implementation helper for GETINFO: knows how to enumerate hidden services + * created via the control port. */ +static int +getinfo_helper_onions(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + smartlist_t *onion_list = NULL; + + if (control_conn && !strcmp(question, "onions/current")) { + onion_list = control_conn->ephemeral_onion_services; + } else if (!strcmp(question, "onions/detached")) { + onion_list = detached_onion_services; + } else { + return 0; + } + if (!onion_list || smartlist_len(onion_list) == 0) { + if (errmsg) { + *errmsg = "No onion services of the specified type."; + } + return -1; + } + if (answer) { + *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL); + } + + return 0; +} + +/** Implementation helper for GETINFO: answers queries about network + * liveness. */ +static int +getinfo_helper_liveness(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + (void)control_conn; + (void)errmsg; + if (strcmp(question, "network-liveness") == 0) { + if (get_cached_network_liveness()) { + *answer = tor_strdup("up"); + } else { + *answer = tor_strdup("down"); + } + } + + return 0; +} + /** Callback function for GETINFO: on a given control connection, try to * answer the question <b>q</b> and store the newly-allocated answer in * *<b>a</b>. If an internal error occurs, return -1 and optionally set @@ -2176,6 +2503,10 @@ static const getinfo_item_t getinfo_items[] = { PREFIX("md/id/", dir, "Microdescriptors by ID"), PREFIX("md/name/", dir, "Microdescriptors by name"), PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."), + PREFIX("hs/client/desc/id", dir, + "Hidden Service descriptor in client's cache by onion."), + PREFIX("hs/service/desc/id/", dir, + "Hidden Service descriptor in services's cache by onion."), PREFIX("net/listeners/", listeners, "Bound addresses by type"), ITEM("ns/all", networkstatus, "Brief summary of router status (v2 directory format)"), @@ -2189,6 +2520,8 @@ static const getinfo_item_t getinfo_items[] = { "Information about and from the ns consensus."), ITEM("network-status", dir, "Brief summary of router status (v1 directory format)"), + ITEM("network-liveness", liveness, + "Current opinion on whether the network is live"), ITEM("circuit-status", events, "List of current circuits originating here."), ITEM("stream-status", events,"List of current streams."), ITEM("orconn-status", events, "A list of current OR connections."), @@ -2210,6 +2543,8 @@ static const getinfo_item_t getinfo_items[] = { "The last bootstrap phase status event that Tor sent."), DOC("status/clients-seen", "Breakdown of client countries seen by a bridge."), + DOC("status/fresh-relay-descs", + "A fresh relay/ei descriptor pair for Tor's current state. Not stored."), DOC("status/version/recommended", "List of currently recommended versions."), DOC("status/version/current", "Status of the current version."), DOC("status/version/num-versioning", "Number of versioning authorities."), @@ -2235,10 +2570,20 @@ static const getinfo_item_t getinfo_items[] = { "v3 Networkstatus consensus as retrieved from a DirPort."), ITEM("exit-policy/default", policies, "The default value appended to the configured exit policy."), + ITEM("exit-policy/reject-private/default", policies, + "The default rules appended to the configured exit policy by" + " ExitPolicyRejectPrivate."), + ITEM("exit-policy/reject-private/relay", policies, + "The relay-specific rules appended to the configured exit policy by" + " ExitPolicyRejectPrivate."), ITEM("exit-policy/full", policies, "The entire exit policy of onion router"), ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"), ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"), PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"), + ITEM("onions/current", onions, + "Onion services owned by the current control connection."), + ITEM("onions/detached", onions, + "Onion services detached from the control connection."), { NULL, NULL, NULL, 0 } }; @@ -2527,12 +2872,26 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, } /* now circ refers to something that is ready to be extended */ + int first_node = zero_circ; SMARTLIST_FOREACH(nodes, const node_t *, node, { - extend_info_t *info = extend_info_from_node(node, 0); - tor_assert(info); /* True, since node_has_descriptor(node) == true */ + extend_info_t *info = extend_info_from_node(node, first_node); + if (first_node && !info) { + log_warn(LD_CONTROL, + "controller tried to connect to a node that doesn't have any " + "addresses that are allowed by the firewall configuration; " + "circuit marked for closing."); + circuit_mark_for_close(TO_CIRCUIT(circ), -END_CIRC_REASON_CONNECTFAILED); + connection_write_str_to_buf("551 Couldn't start circuit\r\n", conn); + goto done; + } else { + /* True, since node_has_descriptor(node) == true and we are extending + * to the node's primary address */ + tor_assert(info); + } circuit_append_new_exit(circ, info); extend_info_free(info); + first_node = 0; }); /* now that we've populated the cpath, start extending */ @@ -2674,6 +3033,7 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len, edge_conn->end_reason = 0; if (tmpcirc) circuit_detach_stream(tmpcirc, edge_conn); + CONNECTION_AP_EXPECT_NONPENDING(ap_conn); TO_CONN(edge_conn)->state = AP_CONN_STATE_CONTROLLER_WAIT; } @@ -2733,12 +3093,14 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len, uint8_t purpose = ROUTER_PURPOSE_GENERAL; int cache = 0; /* eventually, we may switch this to 1 */ - char *cp = memchr(body, '\n', len); + const char *cp = memchr(body, '\n', len); smartlist_t *args = smartlist_new(); tor_assert(cp); - *cp++ = '\0'; + ++cp; - smartlist_split_string(args, body, " ", + char *cmdline = tor_memdup_nulterm(body, cp-body); + + smartlist_split_string(args, cmdline, " ", SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); SMARTLIST_FOREACH_BEGIN(args, char *, option) { if (!strcasecmpstart(option, "purpose=")) { @@ -2787,6 +3149,7 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len, done: SMARTLIST_FOREACH(args, char *, arg, tor_free(arg)); smartlist_free(args); + tor_free(cmdline); return 0; } @@ -3102,8 +3465,7 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len, tor_free(client_nonce); return -1; } - - tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN)); + crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN); /* Now compute and send the server-to-controller response, and the * server's nonce. */ @@ -3211,6 +3573,570 @@ handle_control_dropguards(control_connection_t *conn, return 0; } +/** Implementation for the HSFETCH command. */ +static int +handle_control_hsfetch(control_connection_t *conn, uint32_t len, + const char *body) +{ + int i; + char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL; + smartlist_t *args = NULL, *hsdirs = NULL; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + static const char *hsfetch_command = "HSFETCH"; + static const char *v2_str = "v2-"; + const size_t v2_str_len = strlen(v2_str); + rend_data_t *rend_query = NULL; + + /* Make sure we have at least one argument, the HSAddress. */ + args = getargs_helper(hsfetch_command, conn, body, 1, -1); + if (!args) { + goto exit; + } + + /* Extract the first argument (either HSAddress or DescID). */ + arg1 = smartlist_get(args, 0); + /* Test if it's an HS address without the .onion part. */ + if (rend_valid_service_id(arg1)) { + hsaddress = arg1; + } else if (strcmpstart(arg1, v2_str) == 0 && + rend_valid_descriptor_id(arg1 + v2_str_len) && + base32_decode(digest, sizeof(digest), arg1 + v2_str_len, + REND_DESC_ID_V2_LEN_BASE32) == 0) { + /* We have a well formed version 2 descriptor ID. Keep the decoded value + * of the id. */ + desc_id = digest; + } else { + connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n", + arg1); + goto done; + } + + static const char *opt_server = "SERVER="; + + /* Skip first argument because it's the HSAddress or DescID. */ + for (i = 1; i < smartlist_len(args); ++i) { + const char *arg = smartlist_get(args, i); + const node_t *node; + + if (!strcasecmpstart(arg, opt_server)) { + const char *server; + + server = arg + strlen(opt_server); + node = node_get_by_hex_id(server); + if (!node) { + connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", + server); + goto done; + } + if (!hsdirs) { + /* Stores routerstatus_t object for each specified server. */ + hsdirs = smartlist_new(); + } + /* Valid server, add it to our local list. */ + smartlist_add(hsdirs, node->rs); + } else { + connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n", + arg); + goto done; + } + } + + rend_query = rend_data_client_create(hsaddress, desc_id, NULL, + REND_NO_AUTH); + if (rend_query == NULL) { + connection_printf_to_buf(conn, "551 Error creating the HS query\r\n"); + goto done; + } + + /* Using a descriptor ID, we force the user to provide at least one + * hsdir server using the SERVER= option. */ + if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) { + connection_printf_to_buf(conn, "512 %s option is required\r\n", + opt_server); + goto done; + } + + /* We are about to trigger HSDir fetch so send the OK now because after + * that 650 event(s) are possible so better to have the 250 OK before them + * to avoid out of order replies. */ + send_control_done(conn); + + /* Trigger the fetch using the built rend query and possibly a list of HS + * directory to use. This function ignores the client cache thus this will + * always send a fetch command. */ + rend_client_fetch_v2_desc(rend_query, hsdirs); + + done: + SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); + smartlist_free(args); + /* Contains data pointer that we don't own thus no cleanup. */ + smartlist_free(hsdirs); + rend_data_free(rend_query); + exit: + return 0; +} + +/** Implementation for the HSPOST command. */ +static int +handle_control_hspost(control_connection_t *conn, + uint32_t len, + const char *body) +{ + static const char *opt_server = "SERVER="; + smartlist_t *args = smartlist_new(); + smartlist_t *hs_dirs = NULL; + const char *encoded_desc = body; + size_t encoded_desc_len = len; + + char *cp = memchr(body, '\n', len); + char *argline = tor_strndup(body, cp-body); + + /* If any SERVER= options were specified, try parse the options line */ + if (!strcasecmpstart(argline, opt_server)) { + /* encoded_desc begins after a newline character */ + cp = cp + 1; + encoded_desc = cp; + encoded_desc_len = len-(cp-body); + + smartlist_split_string(args, argline, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + SMARTLIST_FOREACH_BEGIN(args, const char *, arg) { + if (!strcasecmpstart(arg, opt_server)) { + const char *server = arg + strlen(opt_server); + const node_t *node = node_get_by_hex_id(server); + + if (!node || !node->rs) { + connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", + server); + goto done; + } + if (!node->rs->is_hs_dir) { + connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir" + "\r\n", server); + goto done; + } + /* Valid server, add it to our local list. */ + if (!hs_dirs) + hs_dirs = smartlist_new(); + smartlist_add(hs_dirs, node->rs); + } else { + connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n", + arg); + goto done; + } + } SMARTLIST_FOREACH_END(arg); + } + + /* Read the dot encoded descriptor, and parse it. */ + rend_encoded_v2_service_descriptor_t *desc = + tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t)); + read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str); + + rend_service_descriptor_t *parsed = NULL; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc->desc_str, 1)) { + /* Post the descriptor. */ + char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; + if (!rend_get_service_id(parsed->pk, serviceid)) { + smartlist_t *descs = smartlist_new(); + smartlist_add(descs, desc); + + /* We are about to trigger HS descriptor upload so send the OK now + * because after that 650 event(s) are possible so better to have the + * 250 OK before them to avoid out of order replies. */ + send_control_done(conn); + + /* Trigger the descriptor upload */ + directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0); + smartlist_free(descs); + } + + rend_service_descriptor_free(parsed); + } else { + connection_printf_to_buf(conn, "554 Invalid descriptor\r\n"); + } + + tor_free(intro_content); + rend_encoded_v2_service_descriptor_free(desc); + done: + tor_free(argline); + smartlist_free(hs_dirs); /* Contents belong to the rend service code. */ + SMARTLIST_FOREACH(args, char *, arg, tor_free(arg)); + smartlist_free(args); + return 0; +} + +/** Called when we get a ADD_ONION command; parse the body, and set up + * the new ephemeral Onion Service. */ +static int +handle_control_add_onion(control_connection_t *conn, + uint32_t len, + const char *body) +{ + smartlist_t *args; + size_t arg_len; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + args = getargs_helper("ADD_ONION", conn, body, 2, -1); + if (!args) + return 0; + arg_len = smartlist_len(args); + + /* Parse all of the arguments that do not involve handling cryptographic + * material first, since there's no reason to touch that at all if any of + * the other arguments are malformed. + */ + smartlist_t *port_cfgs = smartlist_new(); + int discard_pk = 0; + int detach = 0; + int max_streams = 0; + int max_streams_close_circuit = 0; + for (size_t i = 1; i < arg_len; i++) { + static const char *port_prefix = "Port="; + static const char *flags_prefix = "Flags="; + static const char *max_s_prefix = "MaxStreams="; + + const char *arg = smartlist_get(args, i); + if (!strcasecmpstart(arg, port_prefix)) { + /* "Port=VIRTPORT[,TARGET]". */ + const char *port_str = arg + strlen(port_prefix); + + rend_service_port_config_t *cfg = + rend_service_parse_port_config(port_str, ",", NULL); + if (!cfg) { + connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n"); + goto out; + } + smartlist_add(port_cfgs, cfg); + } else if (!strcasecmpstart(arg, max_s_prefix)) { + /* "MaxStreams=[0..65535]". */ + const char *max_s_str = arg + strlen(max_s_prefix); + int ok = 0; + max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL); + if (!ok) { + connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n"); + goto out; + } + } else if (!strcasecmpstart(arg, flags_prefix)) { + /* "Flags=Flag[,Flag]", where Flag can be: + * * 'DiscardPK' - If tor generates the keypair, do not include it in + * the response. + * * 'Detach' - Do not tie this onion service to any particular control + * connection. + * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is + * exceeded. + */ + static const char *discard_flag = "DiscardPK"; + static const char *detach_flag = "Detach"; + static const char *max_s_close_flag = "MaxStreamsCloseCircuit"; + + smartlist_t *flags = smartlist_new(); + int bad = 0; + + smartlist_split_string(flags, arg + strlen(flags_prefix), ",", + SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(flags) < 1) { + connection_printf_to_buf(conn, "512 Invalid 'Flags' argument\r\n"); + bad = 1; + } + SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) + { + if (!strcasecmp(flag, discard_flag)) { + discard_pk = 1; + } else if (!strcasecmp(flag, detach_flag)) { + detach = 1; + } else if (!strcasecmp(flag, max_s_close_flag)) { + max_streams_close_circuit = 1; + } else { + connection_printf_to_buf(conn, + "512 Invalid 'Flags' argument: %s\r\n", + escaped(flag)); + bad = 1; + break; + } + } SMARTLIST_FOREACH_END(flag); + SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp)); + smartlist_free(flags); + if (bad) + goto out; + } else { + connection_printf_to_buf(conn, "513 Invalid argument\r\n"); + goto out; + } + } + if (smartlist_len(port_cfgs) == 0) { + connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n"); + goto out; + } + + /* Parse the "keytype:keyblob" argument. */ + crypto_pk_t *pk = NULL; + const char *key_new_alg = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + + pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, + &err_msg); + if (!pk) { + if (err_msg) { + connection_write_str_to_buf(err_msg, conn); + tor_free(err_msg); + } + goto out; + } + tor_assert(!err_msg); + + /* Create the HS, using private key pk, and port config port_cfg. + * rend_service_add_ephemeral() will take ownership of pk and port_cfg, + * regardless of success/failure. + */ + char *service_id = NULL; + int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, + max_streams_close_circuit, + &service_id); + port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ + switch (ret) { + case RSAE_OKAY: + { + char *buf = NULL; + tor_assert(service_id); + if (key_new_alg) { + tor_assert(key_new_blob); + tor_asprintf(&buf, + "250-ServiceID=%s\r\n" + "250-PrivateKey=%s:%s\r\n" + "250 OK\r\n", + service_id, + key_new_alg, + key_new_blob); + } else { + tor_asprintf(&buf, + "250-ServiceID=%s\r\n" + "250 OK\r\n", + service_id); + } + if (detach) { + if (!detached_onion_services) + detached_onion_services = smartlist_new(); + smartlist_add(detached_onion_services, service_id); + } else { + if (!conn->ephemeral_onion_services) + conn->ephemeral_onion_services = smartlist_new(); + smartlist_add(conn->ephemeral_onion_services, service_id); + } + + connection_write_str_to_buf(buf, conn); + memwipe(buf, 0, strlen(buf)); + tor_free(buf); + break; + } + case RSAE_BADPRIVKEY: + connection_printf_to_buf(conn, "551 Failed to generate onion address\r\n"); + break; + case RSAE_ADDREXISTS: + connection_printf_to_buf(conn, "550 Onion address collision\r\n"); + break; + case RSAE_BADVIRTPORT: + connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n"); + break; + case RSAE_INTERNAL: /* FALLSTHROUGH */ + default: + connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n"); + } + if (key_new_blob) { + memwipe(key_new_blob, 0, strlen(key_new_blob)); + tor_free(key_new_blob); + } + + out: + if (port_cfgs) { + SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p, + rend_service_port_config_free(p)); + smartlist_free(port_cfgs); + } + + SMARTLIST_FOREACH(args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(args); + return 0; +} + +/** Helper function to handle parsing the KeyType:KeyBlob argument to the + * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated + * and the private key not discarded, the algorithm and serialized private key, + * or NULL and an optional control protocol error message on failure. The + * caller is responsible for freeing the returned key_new_blob and err_msg. + * + * Note: The error messages returned are deliberately vague to avoid echoing + * key material. + */ +STATIC crypto_pk_t * +add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, char **key_new_blob_out, + char **err_msg_out) +{ + smartlist_t *key_args = smartlist_new(); + crypto_pk_t *pk = NULL; + const char *key_new_alg = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + int ok = 0; + + smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); + if (smartlist_len(key_args) != 2) { + err_msg = tor_strdup("512 Invalid key type/blob\r\n"); + goto err; + } + + /* The format is "KeyType:KeyBlob". */ + static const char *key_type_new = "NEW"; + static const char *key_type_best = "BEST"; + static const char *key_type_rsa1024 = "RSA1024"; + + const char *key_type = smartlist_get(key_args, 0); + const char *key_blob = smartlist_get(key_args, 1); + + if (!strcasecmp(key_type_rsa1024, key_type)) { + /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */ + pk = crypto_pk_base64_decode(key_blob, strlen(key_blob)); + if (!pk) { + err_msg = tor_strdup("512 Failed to decode RSA key\r\n"); + goto err; + } + if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + err_msg = tor_strdup("512 Invalid RSA key size\r\n"); + goto err; + } + } else if (!strcasecmp(key_type_new, key_type)) { + /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ + if (!strcasecmp(key_type_rsa1024, key_blob) || + !strcasecmp(key_type_best, key_blob)) { + /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */ + pk = crypto_pk_new(); + if (crypto_pk_generate_key(pk)) { + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_rsa1024); + goto err; + } + if (!discard_pk) { + if (crypto_pk_base64_encode(pk, &key_new_blob)) { + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_rsa1024); + goto err; + } + key_new_alg = key_type_rsa1024; + } + } else { + err_msg = tor_strdup("513 Invalid key type\r\n"); + goto err; + } + } else { + err_msg = tor_strdup("513 Invalid key type\r\n"); + goto err; + } + + /* Succeded in loading or generating a private key. */ + tor_assert(pk); + ok = 1; + + err: + SMARTLIST_FOREACH(key_args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(key_args); + + if (!ok) { + crypto_pk_free(pk); + pk = NULL; + } + if (err_msg_out) { + *err_msg_out = err_msg; + } else { + tor_free(err_msg); + } + *key_new_alg_out = key_new_alg; + *key_new_blob_out = key_new_blob; + + return pk; +} + +/** Called when we get a DEL_ONION command; parse the body, and remove + * the existing ephemeral Onion Service. */ +static int +handle_control_del_onion(control_connection_t *conn, + uint32_t len, + const char *body) +{ + smartlist_t *args; + (void) len; /* body is nul-terminated; it's safe to ignore the length */ + args = getargs_helper("DEL_ONION", conn, body, 1, 1); + if (!args) + return 0; + + const char *service_id = smartlist_get(args, 0); + if (!rend_valid_service_id(service_id)) { + connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); + goto out; + } + + /* Determine if the onion service belongs to this particular control + * connection, or if it is in the global list of detached services. If it + * is in neither, either the service ID is invalid in some way, or it + * explicitly belongs to a different control connection, and an error + * should be returned. + */ + smartlist_t *services[2] = { + conn->ephemeral_onion_services, + detached_onion_services + }; + smartlist_t *onion_services = NULL; + int idx = -1; + for (size_t i = 0; i < ARRAY_LENGTH(services); i++) { + idx = smartlist_string_pos(services[i], service_id); + if (idx != -1) { + onion_services = services[i]; + break; + } + } + if (onion_services == NULL) { + connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); + } else { + int ret = rend_service_del_ephemeral(service_id); + if (ret) { + /* This should *NEVER* fail, since the service is on either the + * per-control connection list, or the global one. + */ + log_warn(LD_BUG, "Failed to remove Onion Service %s.", + escaped(service_id)); + tor_fragile_assert(); + } + + /* Remove/scrub the service_id from the appropriate list. */ + char *cp = smartlist_get(onion_services, idx); + smartlist_del(onion_services, idx); + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + + send_control_done(conn); + } + + out: + SMARTLIST_FOREACH(args, char *, cp, { + memwipe(cp, 0, strlen(cp)); + tor_free(cp); + }); + smartlist_free(args); + return 0; +} + /** Called when <b>conn</b> has no more bytes left on its outbuf. */ int connection_control_finished_flushing(control_connection_t *conn) @@ -3230,10 +4156,6 @@ connection_control_reached_eof(control_connection_t *conn) return 0; } -static void lost_owning_controller(const char *owner_type, - const char *loss_manner) - ATTR_NORETURN; - /** Shut down this Tor instance in the same way that SIGINT would, but * with a log message appropriate for the loss of an owning controller. */ static void @@ -3242,10 +4164,7 @@ lost_owning_controller(const char *owner_type, const char *loss_manner) log_notice(LD_CONTROL, "Owning controller %s has %s -- exiting now.", owner_type, loss_manner); - /* XXXX Perhaps this chunk of code should be a separate function, - * called here and by process_signal(SIGINT). */ - tor_cleanup(); - exit(0); + activate_signal(SIGTERM); } /** Called when <b>conn</b> is being freed. */ @@ -3257,6 +4176,15 @@ connection_control_closed(control_connection_t *conn) conn->event_mask = 0; control_update_global_event_mask(); + /* Close all ephemeral Onion Services if any. + * The list and it's contents are scrubbed/freed in connection_free_. + */ + if (conn->ephemeral_onion_services) { + SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { + rend_service_del_ephemeral(cp); + }); + } + if (conn->is_owning_control_connection) { lost_owning_controller("connection", "closed"); } @@ -3508,6 +4436,22 @@ connection_control_process_inbuf(control_connection_t *conn) } else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) { if (handle_control_dropguards(conn, cmd_data_len, args)) return -1; + } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) { + if (handle_control_hsfetch(conn, cmd_data_len, args)) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) { + if (handle_control_hspost(conn, cmd_data_len, args)) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) { + int ret = handle_control_add_onion(conn, cmd_data_len, args); + memwipe(args, 0, cmd_data_len); /* Scrub the private key. */ + if (ret) + return -1; + } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) { + int ret = handle_control_del_onion(conn, cmd_data_len, args); + memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */ + if (ret) + return -1; } else { connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n", conn->incoming_cmd); @@ -3561,7 +4505,7 @@ control_event_circuit_status(origin_circuit_t *circ, circuit_status_event_t tp, { char *circdesc = circuit_describe_status_for_controller(circ); const char *sp = strlen(circdesc) ? " " : ""; - send_control_event(EVENT_CIRCUIT_STATUS, ALL_FORMATS, + send_control_event(EVENT_CIRCUIT_STATUS, "650 CIRC %lu %s%s%s%s\r\n", (unsigned long)circ->global_identifier, status, sp, @@ -3632,7 +4576,7 @@ control_event_circuit_status_minor(origin_circuit_t *circ, { char *circdesc = circuit_describe_status_for_controller(circ); const char *sp = strlen(circdesc) ? " " : ""; - send_control_event(EVENT_CIRCUIT_STATUS_MINOR, ALL_FORMATS, + send_control_event(EVENT_CIRCUIT_STATUS_MINOR, "650 CIRC_MINOR %lu %s%s%s%s\r\n", (unsigned long)circ->global_identifier, event_desc, sp, @@ -3807,7 +4751,7 @@ control_event_stream_status(entry_connection_t *conn, stream_status_event_t tp, circ = circuit_get_by_edge_conn(ENTRY_TO_EDGE_CONN(conn)); if (circ && CIRCUIT_IS_ORIGIN(circ)) origin_circ = TO_ORIGIN_CIRCUIT(circ); - send_control_event(EVENT_STREAM_STATUS, ALL_FORMATS, + send_control_event(EVENT_STREAM_STATUS, "650 STREAM "U64_FORMAT" %s %lu %s%s%s%s\r\n", U64_PRINTF_ARG(ENTRY_TO_CONN(conn)->global_identifier), status, @@ -3879,7 +4823,7 @@ control_event_or_conn_status(or_connection_t *conn, or_conn_status_event_t tp, } orconn_target_get_name(name, sizeof(name), conn); - send_control_event(EVENT_OR_CONN_STATUS, ALL_FORMATS, + send_control_event(EVENT_OR_CONN_STATUS, "650 ORCONN %s %s%s%s%s ID="U64_FORMAT"\r\n", name, status, reason ? " REASON=" : "", @@ -3902,7 +4846,7 @@ control_event_stream_bandwidth(edge_connection_t *edge_conn) if (!edge_conn->n_read && !edge_conn->n_written) return 0; - send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS, + send_control_event(EVENT_STREAM_BANDWIDTH_USED, "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n", U64_PRINTF_ARG(edge_conn->base_.global_identifier), (unsigned long)edge_conn->n_read, @@ -3937,7 +4881,7 @@ control_event_stream_bandwidth_used(void) if (!edge_conn->n_read && !edge_conn->n_written) continue; - send_control_event(EVENT_STREAM_BANDWIDTH_USED, ALL_FORMATS, + send_control_event(EVENT_STREAM_BANDWIDTH_USED, "650 STREAM_BW "U64_FORMAT" %lu %lu\r\n", U64_PRINTF_ARG(edge_conn->base_.global_identifier), (unsigned long)edge_conn->n_read, @@ -3966,7 +4910,7 @@ control_event_circ_bandwidth_used(void) ocirc = TO_ORIGIN_CIRCUIT(circ); if (!ocirc->n_read_circ_bw && !ocirc->n_written_circ_bw) continue; - send_control_event(EVENT_CIRC_BANDWIDTH_USED, ALL_FORMATS, + send_control_event(EVENT_CIRC_BANDWIDTH_USED, "650 CIRC_BW ID=%d READ=%lu WRITTEN=%lu\r\n", ocirc->global_identifier, (unsigned long)ocirc->n_read_circ_bw, @@ -4002,7 +4946,7 @@ control_event_conn_bandwidth(connection_t *conn) default: return 0; } - send_control_event(EVENT_CONN_BW, ALL_FORMATS, + send_control_event(EVENT_CONN_BW, "650 CONN_BW ID="U64_FORMAT" TYPE=%s " "READ=%lu WRITTEN=%lu\r\n", U64_PRINTF_ARG(conn->global_identifier), @@ -4035,7 +4979,7 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) { memset(cell_stats, 0, sizeof(cell_stats_t)); SMARTLIST_FOREACH_BEGIN(circ->testing_cell_stats, - testing_cell_stats_entry_t *, ent) { + const testing_cell_stats_entry_t *, ent) { tor_assert(ent->command <= CELL_COMMAND_MAX_); if (!ent->removed && !ent->exitward) { cell_stats->added_cells_appward[ent->command] += 1; @@ -4048,10 +4992,8 @@ sum_up_cell_stats_by_command(circuit_t *circ, cell_stats_t *cell_stats) cell_stats->removed_cells_exitward[ent->command] += 1; cell_stats->total_time_exitward[ent->command] += ent->waiting_time * 10; } - tor_free(ent); } SMARTLIST_FOREACH_END(ent); - smartlist_free(circ->testing_cell_stats); - circ->testing_cell_stats = NULL; + circuit_clear_testing_cell_stats(circ); } /** Helper: append a cell statistics string to <code>event_parts</code>, @@ -4149,7 +5091,7 @@ control_event_circuit_cell_stats(void) continue; sum_up_cell_stats_by_command(circ, cell_stats); format_cell_stats(&event_string, circ, cell_stats); - send_control_event(EVENT_CELL_STATS, ALL_FORMATS, + send_control_event(EVENT_CELL_STATS, "650 CELL_STATS %s\r\n", event_string); tor_free(event_string); } @@ -4171,7 +5113,7 @@ control_event_tb_empty(const char *bucket, uint32_t read_empty_time, if (get_options()->TestingEnableTbEmptyEvent && EVENT_IS_INTERESTING(EVENT_TB_EMPTY) && (read_empty_time > 0 || write_empty_time > 0)) { - send_control_event(EVENT_TB_EMPTY, ALL_FORMATS, + send_control_event(EVENT_TB_EMPTY, "650 TB_EMPTY %s READ=%d WRITTEN=%d " "LAST=%d\r\n", bucket, read_empty_time, write_empty_time, @@ -4204,7 +5146,7 @@ control_event_bandwidth_used(uint32_t n_read, uint32_t n_written) ++n_measurements; if (EVENT_IS_INTERESTING(EVENT_BANDWIDTH_USED)) { - send_control_event(EVENT_BANDWIDTH_USED, ALL_FORMATS, + send_control_event(EVENT_BANDWIDTH_USED, "650 BW %lu %lu\r\n", (unsigned long)n_read, (unsigned long)n_written); @@ -4303,7 +5245,11 @@ control_event_logmsg(int severity, uint32_t domain, const char *msg) default: s = "UnknownLogSeverity"; break; } ++disable_log_messages; - send_control_event(event, ALL_FORMATS, "650 %s %s\r\n", s, b?b:msg); + send_control_event(event, "650 %s %s\r\n", s, b?b:msg); + if (severity == LOG_ERR) { + /* Force a flush, since we may be about to die horribly */ + queued_events_flush_all(1); + } --disable_log_messages; tor_free(b); } @@ -4331,7 +5277,7 @@ control_event_descriptors_changed(smartlist_t *routers) }); ids = smartlist_join_strings(names, " ", 0, NULL); tor_asprintf(&msg, "650 NEWDESC %s\r\n", ids); - send_control_event_string(EVENT_NEW_DESC, ALL_FORMATS, msg); + send_control_event_string(EVENT_NEW_DESC, msg); tor_free(ids); tor_free(msg); SMARTLIST_FOREACH(names, char *, cp, tor_free(cp)); @@ -4353,7 +5299,7 @@ control_event_address_mapped(const char *from, const char *to, time_t expires, return 0; if (expires < 3 || expires == TIME_MAX) - send_control_event(EVENT_ADDRMAP, ALL_FORMATS, + send_control_event(EVENT_ADDRMAP, "650 ADDRMAP %s %s NEVER %s%s" "CACHED=\"%s\"\r\n", from, to, error?error:"", error?" ":"", @@ -4363,7 +5309,7 @@ control_event_address_mapped(const char *from, const char *to, time_t expires, char buf2[ISO_TIME_LEN+1]; format_local_iso_time(buf,expires); format_iso_time(buf2,expires); - send_control_event(EVENT_ADDRMAP, ALL_FORMATS, + send_control_event(EVENT_ADDRMAP, "650 ADDRMAP %s %s \"%s\"" " %s%sEXPIRES=\"%s\" CACHED=\"%s\"\r\n", from, to, buf, @@ -4405,9 +5351,9 @@ control_event_or_authdir_new_descriptor(const char *action, buf = tor_malloc(totallen); strlcpy(buf, firstline, totallen); strlcpy(buf+strlen(firstline), esc, totallen); - send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS, + send_control_event_string(EVENT_AUTHDIR_NEWDESCS, buf); - send_control_event_string(EVENT_AUTHDIR_NEWDESCS, ALL_FORMATS, + send_control_event_string(EVENT_AUTHDIR_NEWDESCS, "650 OK\r\n"); tor_free(esc); tor_free(buf); @@ -4415,6 +5361,52 @@ control_event_or_authdir_new_descriptor(const char *action, return 0; } +/** Cached liveness for network liveness events and GETINFO + */ + +static int network_is_live = 0; + +static int +get_cached_network_liveness(void) +{ + return network_is_live; +} + +static void +set_cached_network_liveness(int liveness) +{ + network_is_live = liveness; +} + +/** The network liveness has changed; this is called from circuitstats.c + * whenever we receive a cell, or when timeout expires and we assume the + * network is down. */ +int +control_event_network_liveness_update(int liveness) +{ + if (liveness > 0) { + if (get_cached_network_liveness() <= 0) { + /* Update cached liveness */ + set_cached_network_liveness(1); + log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP"); + send_control_event_string(EVENT_NETWORK_LIVENESS, + "650 NETWORK_LIVENESS UP\r\n"); + } + /* else was already live, no-op */ + } else { + if (get_cached_network_liveness() > 0) { + /* Update cached liveness */ + set_cached_network_liveness(0); + log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN"); + send_control_event_string(EVENT_NETWORK_LIVENESS, + "650 NETWORK_LIVENESS DOWN\r\n"); + } + /* else was already dead, no-op */ + } + + return 0; +} + /** Helper function for NS-style events. Constructs and sends an event * of type <b>event</b> with string <b>event_string</b> out of the set of * networkstatuses <b>statuses</b>. Currently it is used for NS events @@ -4445,8 +5437,8 @@ control_event_networkstatus_changed_helper(smartlist_t *statuses, SMARTLIST_FOREACH(strs, char *, cp, tor_free(cp)); smartlist_free(strs); tor_free(s); - send_control_event_string(event, ALL_FORMATS, esc); - send_control_event_string(event, ALL_FORMATS, + send_control_event_string(event, esc); + send_control_event_string(event, "650 OK\r\n"); tor_free(esc); @@ -4503,7 +5495,7 @@ control_event_buildtimeout_set(buildtimeout_set_event_t type, break; } - send_control_event(EVENT_BUILDTIMEOUT_SET, ALL_FORMATS, + send_control_event(EVENT_BUILDTIMEOUT_SET, "650 BUILDTIMEOUT_SET %s %s\r\n", type_string, args); @@ -4516,7 +5508,7 @@ control_event_signal(uintptr_t signal) { const char *signal_string = NULL; - if (!control_event_is_interesting(EVENT_SIGNAL)) + if (!control_event_is_interesting(EVENT_GOT_SIGNAL)) return 0; switch (signal) { @@ -4544,7 +5536,7 @@ control_event_signal(uintptr_t signal) return -1; } - send_control_event(EVENT_SIGNAL, ALL_FORMATS, "650 SIGNAL %s\r\n", + send_control_event(EVENT_GOT_SIGNAL, "650 SIGNAL %s\r\n", signal_string); return 0; } @@ -4572,7 +5564,7 @@ control_event_networkstatus_changed_single(const routerstatus_t *rs) int control_event_my_descriptor_changed(void) { - send_control_event(EVENT_DESCCHANGED, ALL_FORMATS, "650 DESCCHANGED\r\n"); + send_control_event(EVENT_DESCCHANGED, "650 DESCCHANGED\r\n"); return 0; } @@ -4622,24 +5614,40 @@ control_event_status(int type, int severity, const char *format, va_list args) } tor_vasprintf(&user_buf, format, args); - send_control_event(type, ALL_FORMATS, "%s %s\r\n", format_buf, user_buf); + send_control_event(type, "%s %s\r\n", format_buf, user_buf); tor_free(user_buf); return 0; } +#define CONTROL_EVENT_STATUS_BODY(event, sev) \ + int r; \ + do { \ + va_list ap; \ + if (!EVENT_IS_INTERESTING(event)) \ + return 0; \ + \ + va_start(ap, format); \ + r = control_event_status((event), (sev), format, ap); \ + va_end(ap); \ + } while (0) + /** Format and send an EVENT_STATUS_GENERAL event whose main text is obtained * by formatting the arguments using the printf-style <b>format</b>. */ int control_event_general_status(int severity, const char *format, ...) { - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_GENERAL)) - return 0; + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, severity); + return r; +} - va_start(ap, format); - r = control_event_status(EVENT_STATUS_GENERAL, severity, format, ap); - va_end(ap); +/** Format and send an EVENT_STATUS_GENERAL LOG_ERR event, and flush it to the + * controller(s) immediately. */ +int +control_event_general_error(const char *format, ...) +{ + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_GENERAL, LOG_ERR); + /* Force a flush, since we may be about to die horribly */ + queued_events_flush_all(1); return r; } @@ -4648,14 +5656,18 @@ control_event_general_status(int severity, const char *format, ...) int control_event_client_status(int severity, const char *format, ...) { - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_CLIENT)) - return 0; + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, severity); + return r; +} - va_start(ap, format); - r = control_event_status(EVENT_STATUS_CLIENT, severity, format, ap); - va_end(ap); +/** Format and send an EVENT_STATUS_CLIENT LOG_ERR event, and flush it to the + * controller(s) immediately. */ +int +control_event_client_error(const char *format, ...) +{ + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_CLIENT, LOG_ERR); + /* Force a flush, since we may be about to die horribly */ + queued_events_flush_all(1); return r; } @@ -4664,14 +5676,18 @@ control_event_client_status(int severity, const char *format, ...) int control_event_server_status(int severity, const char *format, ...) { - va_list ap; - int r; - if (!EVENT_IS_INTERESTING(EVENT_STATUS_SERVER)) - return 0; + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, severity); + return r; +} - va_start(ap, format); - r = control_event_status(EVENT_STATUS_SERVER, severity, format, ap); - va_end(ap); +/** Format and send an EVENT_STATUS_SERVER LOG_ERR event, and flush it to the + * controller(s) immediately. */ +int +control_event_server_error(const char *format, ...) +{ + CONTROL_EVENT_STATUS_BODY(EVENT_STATUS_SERVER, LOG_ERR); + /* Force a flush, since we may be about to die horribly */ + queued_events_flush_all(1); return r; } @@ -4695,7 +5711,7 @@ control_event_guard(const char *nickname, const char *digest, } else { tor_snprintf(buf, sizeof(buf), "$%s~%s", hbuf, nickname); } - send_control_event(EVENT_GUARD, ALL_FORMATS, + send_control_event(EVENT_GUARD, "650 GUARD ENTRY %s %s\r\n", buf, status); } return 0; @@ -4726,7 +5742,7 @@ control_event_conf_changed(const smartlist_t *elements) } } result = smartlist_join_strings(lines, "\r\n", 0, NULL); - send_control_event(EVENT_CONF_CHANGED, 0, + send_control_event(EVENT_CONF_CHANGED, "650-CONF_CHANGED\r\n%s\r\n650 OK\r\n", result); tor_free(result); SMARTLIST_FOREACH(lines, char *, cp, tor_free(cp)); @@ -4779,8 +5795,6 @@ static char *owning_controller_process_spec = NULL; * if this Tor instance is not currently owned by a process. */ static tor_process_monitor_t *owning_controller_process_monitor = NULL; -static void owning_controller_procmon_cb(void *unused) ATTR_NORETURN; - /** Process-termination monitor callback for Tor's owning controller * process. */ static void @@ -5118,7 +6132,7 @@ MOCK_IMPL(void, void control_event_clients_seen(const char *controller_str) { - send_control_event(EVENT_CLIENTS_SEEN, 0, + send_control_event(EVENT_CLIENTS_SEEN, "650 CLIENTS_SEEN %s\r\n", controller_str); } @@ -5132,7 +6146,7 @@ void control_event_transport_launched(const char *mode, const char *transport_name, tor_addr_t *addr, uint16_t port) { - send_control_event(EVENT_TRANSPORT_LAUNCHED, ALL_FORMATS, + send_control_event(EVENT_TRANSPORT_LAUNCHED, "650 TRANSPORT_LAUNCHED %s %s %s %u\r\n", mode, transport_name, fmt_addr(addr), port); } @@ -5176,6 +6190,29 @@ node_describe_longname_by_id,(const char *id_digest)) return longname; } +/** Return either the onion address if the given pointer is a non empty + * string else the unknown string. */ +static const char * +rend_hsaddress_str_or_unknown(const char *onion_address) +{ + static const char *str_unknown = "UNKNOWN"; + const char *str_ret = str_unknown; + + /* No valid pointer, unknown it is. */ + if (!onion_address) { + goto end; + } + /* Empty onion address thus we don't know, unknown it is. */ + if (onion_address[0] == '\0') { + goto end; + } + /* All checks are good so return the given onion address. */ + str_ret = onion_address; + + end: + return str_ret; +} + /** send HS_DESC requested event. * * <b>rend_query</b> is used to fetch requested onion address and auth type. @@ -5194,14 +6231,102 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query, return; } - send_control_event(EVENT_HS_DESC, ALL_FORMATS, + send_control_event(EVENT_HS_DESC, "650 HS_DESC REQUESTED %s %s %s %s\r\n", - rend_query->onion_address, + rend_hsaddress_str_or_unknown(rend_query->onion_address), rend_auth_type_to_string(rend_query->auth_type), node_describe_longname_by_id(id_digest), desc_id_base32); } +/** For an HS descriptor query <b>rend_data</b>, using the + * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out + * which descriptor ID in the query is the right one. + * + * Return a pointer of the binary descriptor ID found in the query's object + * or NULL if not found. */ +static const char * +get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) +{ + int replica; + const char *desc_id = NULL; + + /* Possible if the fetch was done using a descriptor ID. This means that + * the HSFETCH command was used. */ + if (!tor_digest_is_zero(rend_data->desc_id_fetch)) { + desc_id = rend_data->desc_id_fetch; + goto end; + } + + /* OK, we have an onion address so now let's find which descriptor ID + * is the one associated with the HSDir fingerprint. */ + for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + replica++) { + const char *digest = rend_data->descriptor_id[replica]; + + SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) { + if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) { + /* Found it! This descriptor ID is the right one. */ + desc_id = digest; + goto end; + } + } SMARTLIST_FOREACH_END(fingerprint); + } + + end: + return desc_id; +} + +/** send HS_DESC CREATED event when a local service generates a descriptor. + * + * <b>service_id</b> is the descriptor onion address. + * <b>desc_id_base32</b> is the descriptor ID. + * <b>replica</b> is the the descriptor replica number. + */ +void +control_event_hs_descriptor_created(const char *service_id, + const char *desc_id_base32, + int replica) +{ + if (!service_id || !desc_id_base32) { + log_warn(LD_BUG, "Called with service_digest==%p, " + "desc_id_base32==%p", service_id, desc_id_base32); + return; + } + + send_control_event(EVENT_HS_DESC, + "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s " + "REPLICA=%d\r\n", + service_id, + desc_id_base32, + replica); +} + +/** send HS_DESC upload event. + * + * <b>service_id</b> is the descriptor onion address. + * <b>hs_dir</b> is the description of contacting hs directory. + * <b>desc_id_base32</b> is the ID of requested hs descriptor. + */ +void +control_event_hs_descriptor_upload(const char *service_id, + const char *id_digest, + const char *desc_id_base32) +{ + if (!service_id || !id_digest || !desc_id_base32) { + log_warn(LD_BUG, "Called with service_digest==%p, " + "desc_id_base32==%p, id_digest==%p", service_id, + desc_id_base32, id_digest); + return; + } + + send_control_event(EVENT_HS_DESC, + "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", + service_id, + node_describe_longname_by_id(id_digest), + desc_id_base32); +} + /** send HS_DESC event after got response from hs directory. * * NOTE: this is an internal function used by following functions: @@ -5212,27 +6337,79 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query, */ void control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, + const char *onion_address, + const rend_data_t *rend_data, const char *id_digest, const char *reason) { + char *desc_id_field = NULL; char *reason_field = NULL; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + const char *desc_id = NULL; - if (!action || !rend_query || !id_digest) { - log_warn(LD_BUG, "Called with action==%p, rend_query==%p, " - "id_digest==%p", action, rend_query, id_digest); + if (!action || !id_digest || !rend_data || !onion_address) { + log_warn(LD_BUG, "Called with action==%p, id_digest==%p, " + "rend_data==%p, onion_address==%p", action, id_digest, + rend_data, onion_address); return; } + desc_id = get_desc_id_from_query(rend_data, id_digest); + if (desc_id != NULL) { + /* Set the descriptor ID digest to base32 so we can send it. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); + /* Extra whitespace is needed before the value. */ + tor_asprintf(&desc_id_field, " %s", desc_id_base32); + } + if (reason) { tor_asprintf(&reason_field, " REASON=%s", reason); } - send_control_event(EVENT_HS_DESC, ALL_FORMATS, - "650 HS_DESC %s %s %s %s%s\r\n", + send_control_event(EVENT_HS_DESC, + "650 HS_DESC %s %s %s %s%s%s\r\n", action, - rend_query->onion_address, - rend_auth_type_to_string(rend_query->auth_type), + rend_hsaddress_str_or_unknown(onion_address), + rend_auth_type_to_string(rend_data->auth_type), + node_describe_longname_by_id(id_digest), + desc_id_field ? desc_id_field : "", + reason_field ? reason_field : ""); + + tor_free(desc_id_field); + tor_free(reason_field); +} + +/** send HS_DESC event after got response from hs directory. + * + * NOTE: this is an internal function used by following functions: + * control_event_hs_descriptor_uploaded + * control_event_hs_descriptor_upload_failed + * + * So do not call this function directly. + */ +void +control_event_hs_descriptor_upload_end(const char *action, + const char *onion_address, + const char *id_digest, + const char *reason) +{ + char *reason_field = NULL; + + if (!action || !id_digest) { + log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action, + id_digest); + return; + } + + if (reason) { + tor_asprintf(&reason_field, " REASON=%s", reason); + } + + send_control_event(EVENT_HS_DESC, + "650 HS_DESC %s %s UNKNOWN %s%s\r\n", + action, + rend_hsaddress_str_or_unknown(onion_address), node_describe_longname_by_id(id_digest), reason_field ? reason_field : ""); @@ -5241,19 +6418,38 @@ control_event_hs_descriptor_receive_end(const char *action, /** send HS_DESC RECEIVED event * - * called when a we successfully received a hidden service descriptor. + * called when we successfully received a hidden service descriptor. */ void -control_event_hs_descriptor_received(const rend_data_t *rend_query, +control_event_hs_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, const char *id_digest) { - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); + if (!rend_data || !id_digest || !onion_address) { + log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, " + "onion_address==%p", rend_data, id_digest, onion_address); return; } - control_event_hs_descriptor_receive_end("RECEIVED", rend_query, - id_digest, NULL); + control_event_hs_descriptor_receive_end("RECEIVED", onion_address, + rend_data, id_digest, NULL); +} + +/** send HS_DESC UPLOADED event + * + * called when we successfully uploaded a hidden service descriptor. + */ +void +control_event_hs_descriptor_uploaded(const char *id_digest, + const char *onion_address) +{ + if (!id_digest) { + log_warn(LD_BUG, "Called with id_digest==%p", + id_digest); + return; + } + + control_event_hs_descriptor_upload_end("UPLOADED", onion_address, + id_digest, NULL); } /** Send HS_DESC event to inform controller that query <b>rend_query</b> @@ -5262,17 +6458,69 @@ control_event_hs_descriptor_received(const rend_data_t *rend_query, * field. */ void -control_event_hs_descriptor_failed(const rend_data_t *rend_query, +control_event_hs_descriptor_failed(const rend_data_t *rend_data, const char *id_digest, const char *reason) { - if (!rend_query || !id_digest) { - log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p", - rend_query, id_digest); + if (!rend_data || !id_digest) { + log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p", + rend_data, id_digest); + return; + } + control_event_hs_descriptor_receive_end("FAILED", + rend_data->onion_address, + rend_data, id_digest, reason); +} + +/** send HS_DESC_CONTENT event after completion of a successful fetch from + * hs directory. */ +void +control_event_hs_descriptor_content(const char *onion_address, + const char *desc_id, + const char *hsdir_id_digest, + const char *content) +{ + static const char *event_name = "HS_DESC_CONTENT"; + char *esc_content = NULL; + + if (!onion_address || !desc_id || !hsdir_id_digest) { + log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, " + "hsdir_id_digest==%p", onion_address, desc_id, hsdir_id_digest); return; } - control_event_hs_descriptor_receive_end("FAILED", rend_query, - id_digest, reason); + + if (content == NULL) { + /* Point it to empty content so it can still be escaped. */ + content = ""; + } + write_escaped_data(content, strlen(content), &esc_content); + + send_control_event(EVENT_HS_DESC_CONTENT, + "650+%s %s %s %s\r\n%s650 OK\r\n", + event_name, + rend_hsaddress_str_or_unknown(onion_address), + desc_id, + node_describe_longname_by_id(hsdir_id_digest), + esc_content); + tor_free(esc_content); +} + +/** Send HS_DESC event to inform controller upload of hidden service + * descriptor identified by <b>id_digest</b> failed. If <b>reason</b> + * is not NULL, add it to REASON= field. + */ +void +control_event_hs_descriptor_upload_failed(const char *id_digest, + const char *onion_address, + const char *reason) +{ + if (!id_digest) { + log_warn(LD_BUG, "Called with id_digest==%p", + id_digest); + return; + } + control_event_hs_descriptor_upload_end("UPLOAD_FAILED", onion_address, + id_digest, reason); } /** Free any leftover allocated memory of the control.c subsystem. */ @@ -5281,6 +6529,20 @@ control_free_all(void) { if (authentication_cookie) /* Free the auth cookie */ tor_free(authentication_cookie); + if (detached_onion_services) { /* Free the detached onion services */ + SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp)); + smartlist_free(detached_onion_services); + } + if (queued_control_events) { + SMARTLIST_FOREACH(queued_control_events, queued_event_t *, ev, + queued_event_free(ev)); + smartlist_free(queued_control_events); + queued_control_events = NULL; + } + if (flush_queued_events_event) { + tor_event_free(flush_queued_events_event); + flush_queued_events_event = NULL; + } } #ifdef TOR_UNIT_TESTS diff --git a/src/or/control.h b/src/or/control.h index 47a601817a..008bfb1c3b 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,6 +12,8 @@ #ifndef TOR_CONTROL_H #define TOR_CONTROL_H +void control_initialize_event_queue(void); + void control_update_global_event_mask(void); void control_adjust_event_log_severity(void); @@ -67,6 +69,7 @@ int control_event_or_authdir_new_descriptor(const char *action, size_t desclen, const char *msg); int control_event_my_descriptor_changed(void); +int control_event_network_liveness_update(int liveness); int control_event_networkstatus_changed(smartlist_t *statuses); int control_event_newconsensus(const networkstatus_t *consensus); @@ -77,6 +80,14 @@ int control_event_client_status(int severity, const char *format, ...) CHECK_PRINTF(2,3); int control_event_server_status(int severity, const char *format, ...) CHECK_PRINTF(2,3); + +int control_event_general_error(const char *format, ...) + CHECK_PRINTF(1,2); +int control_event_client_error(const char *format, ...) + CHECK_PRINTF(1,2); +int control_event_server_error(const char *format, ...) + CHECK_PRINTF(1,2); + int control_event_guard(const char *nickname, const char *digest, const char *status); int control_event_conf_changed(const smartlist_t *elements); @@ -106,15 +117,36 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); void control_event_hs_descriptor_requested(const rend_data_t *rend_query, const char *desc_id_base32, const char *hs_dir); +void control_event_hs_descriptor_created(const char *service_id, + const char *desc_id_base32, + int replica); +void control_event_hs_descriptor_upload(const char *service_id, + const char *desc_id_base32, + const char *hs_dir); void control_event_hs_descriptor_receive_end(const char *action, - const rend_data_t *rend_query, - const char *hs_dir, - const char *reason); -void control_event_hs_descriptor_received(const rend_data_t *rend_query, - const char *hs_dir); -void control_event_hs_descriptor_failed(const rend_data_t *rend_query, - const char *hs_dir, + const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest, + const char *reason); +void control_event_hs_descriptor_upload_end(const char *action, + const char *onion_address, + const char *hs_dir, + const char *reason); +void control_event_hs_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *id_digest); +void control_event_hs_descriptor_uploaded(const char *hs_dir, + const char *onion_address); +void control_event_hs_descriptor_failed(const rend_data_t *rend_data, + const char *id_digest, const char *reason); +void control_event_hs_descriptor_upload_failed(const char *hs_dir, + const char *onion_address, + const char *reason); +void control_event_hs_descriptor_content(const char *onion_address, + const char *desc_id, + const char *hsdir_fp, + const char *content); void control_free_all(void); @@ -123,6 +155,7 @@ void control_free_all(void); * because it is used both as a list of v0 event types, and as indices * into the bitfield to determine which controllers want which events. */ +/* This bitfield has no event zero 0x0000 */ #define EVENT_MIN_ 0x0001 #define EVENT_CIRCUIT_STATUS 0x0001 #define EVENT_STREAM_STATUS 0x0002 @@ -149,7 +182,7 @@ void control_free_all(void); #define EVENT_CLIENTS_SEEN 0x0015 #define EVENT_NEWCONSENSUS 0x0016 #define EVENT_BUILDTIMEOUT_SET 0x0017 -#define EVENT_SIGNAL 0x0018 +#define EVENT_GOT_SIGNAL 0x0018 #define EVENT_CONF_CHANGED 0x0019 #define EVENT_CONN_BW 0x001A #define EVENT_CELL_STATS 0x001B @@ -157,25 +190,42 @@ void control_free_all(void); #define EVENT_CIRC_BANDWIDTH_USED 0x001D #define EVENT_TRANSPORT_LAUNCHED 0x0020 #define EVENT_HS_DESC 0x0021 -#define EVENT_MAX_ 0x0021 -/* If EVENT_MAX_ ever hits 0x003F, we need to make the mask into a +#define EVENT_HS_DESC_CONTENT 0x0022 +#define EVENT_NETWORK_LIVENESS 0x0023 +#define EVENT_MAX_ 0x0023 + +/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */ +#define EVENT_CAPACITY_ 0x0040 + +/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a * different structure, as it can only handle a maximum left shift of 1<<63. */ +#if EVENT_MAX_ >= EVENT_CAPACITY_ +#error control_connection_t.event_mask has an event greater than its capacity +#endif + +#define EVENT_MASK_(e) (((uint64_t)1)<<(e)) + +#define EVENT_MASK_NONE_ ((uint64_t)0x0) + +#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_) +#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \ + >> (EVENT_CAPACITY_ - EVENT_MAX_ \ + - EVENT_MIN_)) + +#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \ + & EVENT_MASK_BELOW_MAX_) + /* Used only by control.c and test.c */ STATIC size_t write_escaped_data(const char *data, size_t len, char **out); STATIC size_t read_escaped_data(const char *data, size_t len, char **out); -/** Flag for event_format_t. Indicates that we should use the one standard - format. (Other formats previous existed, and are now deprecated) - */ -#define ALL_FORMATS 1 -/** Bit field of flags to select how to format a controller event. Recognized - * flag is ALL_FORMATS. */ -typedef int event_format_t; #ifdef TOR_UNIT_TESTS MOCK_DECL(STATIC void, -send_control_event_string,(uint16_t event, event_format_t which, - const char *msg)); + send_control_event_string,(uint16_t event, const char *msg)); + +MOCK_DECL(STATIC void, + queue_control_event_string,(uint16_t event, char *msg)); void control_testing_set_global_event_mask(uint64_t mask); #endif @@ -204,6 +254,11 @@ void append_cell_stats_by_command(smartlist_t *event_parts, void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); + +STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, + char **err_msg_out); #endif #endif diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index d511ecf84c..3109d5a177 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -160,7 +160,7 @@ typedef struct cpuworker_job_u { } u; } cpuworker_job_t; -static int +static workqueue_reply_t update_state_threadfn(void *state_, void *work_) { worker_state_t *state = state_; @@ -387,7 +387,7 @@ cpuworker_onion_handshake_replyfn(void *work_) } /** Implementation function for onion handshake requests. */ -static int +static workqueue_reply_t cpuworker_onion_handshake_threadfn(void *state_, void *work_) { worker_state_t *state = state_; diff --git a/src/or/cpuworker.h b/src/or/cpuworker.h index 70a595e472..62cf0eb164 100644 --- a/src/or/cpuworker.h +++ b/src/or/cpuworker.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/dircollate.c b/src/or/dircollate.c new file mode 100644 index 0000000000..3f9d78f02d --- /dev/null +++ b/src/or/dircollate.c @@ -0,0 +1,327 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dircollate.c + * + * \brief Collation code for figuring out which identities to vote for in + * the directory voting process. + */ + +#define DIRCOLLATE_PRIVATE +#include "dircollate.h" +#include "dirvote.h" + +static void dircollator_collate_by_rsa(dircollator_t *dc); +static void dircollator_collate_by_ed25519(dircollator_t *dc); + +/** Hashtable entry mapping a pair of digests (actually an ed25519 key and an + * RSA SHA1 digest) to an array of vote_routerstatus_t. */ +typedef struct ddmap_entry_s { + HT_ENTRY(ddmap_entry_s) node; + uint8_t d[DIGEST_LEN + DIGEST256_LEN]; + /* The nth member of this array corresponds to the vote_routerstatus_t (if + * any) received for this digest pair from the nth voter. */ + vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER]; +} ddmap_entry_t; + +/** Release all storage held by e. */ +static void +ddmap_entry_free(ddmap_entry_t *e) +{ + tor_free(e); +} + +/** Return a new empty ddmap_entry, with <b>n_votes</b> elements in + * vrs_list. */ +static ddmap_entry_t * +ddmap_entry_new(int n_votes) +{ + return tor_malloc_zero(STRUCT_OFFSET(ddmap_entry_t, vrs_lst) + + sizeof(vote_routerstatus_t *) * n_votes); +} + +static unsigned +ddmap_entry_hash(const ddmap_entry_t *ent) +{ + return (unsigned) siphash24g(ent->d, sizeof(ent->d)); +} + +static unsigned +ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b) +{ + return fast_memeq(a->d, b->d, sizeof(a->d)); +} + +/** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the + * ed25519 identity as <b>ed25519</b>. */ +static void +ddmap_entry_set_digests(ddmap_entry_t *ent, + const uint8_t *rsa_sha1, + const uint8_t *ed25519) +{ + memcpy(ent->d, rsa_sha1, DIGEST_LEN); + memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN); +} + +HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, + ddmap_entry_eq); +HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash, + ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_); + +/** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator + * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of + * its RSA key digest and Ed25519 key. */ +static void +dircollator_add_routerstatus(dircollator_t *dc, + int vote_num, + networkstatus_t *vote, + vote_routerstatus_t *vrs) +{ + const char *id = vrs->status.identity_digest; + + vrs->ed25519_reflects_consensus = 0; + + (void) vote; + vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id); + if (NULL == vrs_lst) { + vrs_lst = tor_calloc(sizeof(vote_routerstatus_t *), dc->n_votes); + digestmap_set(dc->by_rsa_sha1, id, vrs_lst); + } + tor_assert(vrs_lst[vote_num] == NULL); + vrs_lst[vote_num] = vrs; + + const uint8_t *ed = vrs->ed25519_id; + + if (! vrs->has_ed25519_listing) + return; + + ddmap_entry_t search, *found; + memset(&search, 0, sizeof(search)); + ddmap_entry_set_digests(&search, (const uint8_t *)id, ed); + found = HT_FIND(double_digest_map, &dc->by_both_ids, &search); + if (NULL == found) { + found = ddmap_entry_new(dc->n_votes); + ddmap_entry_set_digests(found, (const uint8_t *)id, ed); + HT_INSERT(double_digest_map, &dc->by_both_ids, found); + } + vrs_lst = found->vrs_lst; + tor_assert(vrs_lst[vote_num] == NULL); + vrs_lst[vote_num] = vrs; +} + +/** Create and return a new dircollator object to use when collating + * <b>n_votes</b> out of a total of <b>n_authorities</b>. */ +dircollator_t * +dircollator_new(int n_votes, int n_authorities) +{ + dircollator_t *dc = tor_malloc_zero(sizeof(dircollator_t)); + + tor_assert(n_votes <= n_authorities); + + dc->n_votes = n_votes; + dc->n_authorities = n_authorities; + + dc->by_rsa_sha1 = digestmap_new(); + HT_INIT(double_digest_map, &dc->by_both_ids); + + return dc; +} + +/** Release all storage held by <b>dc</b>. */ +void +dircollator_free(dircollator_t *dc) +{ + if (!dc) + return; + + if (dc->by_collated_rsa_sha1 != dc->by_rsa_sha1) + digestmap_free(dc->by_collated_rsa_sha1, NULL); + + digestmap_free(dc->by_rsa_sha1, tor_free_); + smartlist_free(dc->all_rsa_sha1_lst); + + ddmap_entry_t **e, **next, *this; + for (e = HT_START(double_digest_map, &dc->by_both_ids); + e != NULL; e = next) { + this = *e; + next = HT_NEXT_RMV(double_digest_map, &dc->by_both_ids, e); + ddmap_entry_free(this); + } + HT_CLEAR(double_digest_map, &dc->by_both_ids); + + tor_free(dc); +} + +/** Add a single vote <b>v</b> to a dircollator <b>dc</b>. This function must + * be called exactly once for each vote to be used in the consensus. It may + * only be called before dircollator_collate(). + */ +void +dircollator_add_vote(dircollator_t *dc, networkstatus_t *v) +{ + tor_assert(v->type == NS_TYPE_VOTE); + tor_assert(dc->next_vote_num < dc->n_votes); + tor_assert(!dc->is_collated); + + const int votenum = dc->next_vote_num++; + + SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, vote_routerstatus_t *, vrs) { + dircollator_add_routerstatus(dc, votenum, v, vrs); + } SMARTLIST_FOREACH_END(vrs); +} + +/** Sort the entries in <b>dc</b> according to <b>consensus_method</b>, so + * that the consensus process can iterate over them with + * dircollator_n_routers() and dircollator_get_votes_for_router(). */ +void +dircollator_collate(dircollator_t *dc, int consensus_method) +{ + tor_assert(!dc->is_collated); + dc->all_rsa_sha1_lst = smartlist_new(); + + if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING) + dircollator_collate_by_rsa(dc); + else + dircollator_collate_by_ed25519(dc); + + smartlist_sort_digests(dc->all_rsa_sha1_lst); + dc->is_collated = 1; +} + +/** + * Collation function for RSA-only consensuses: collate the votes for each + * entry in <b>dc</b> by their RSA keys. + * + * The rule is: + * If an RSA identity key is listed by more than half of the authorities, + * include that identity, and treat all descriptors with that RSA identity + * as describing the same router. + */ +static void +dircollator_collate_by_rsa(dircollator_t *dc) +{ + const int total_authorities = dc->n_authorities; + + DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) { + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (vrs_lst[i] != NULL) + ++n; + } + + if (n <= total_authorities / 2) + continue; + + smartlist_add(dc->all_rsa_sha1_lst, (char *)k); + } DIGESTMAP_FOREACH_END; + + dc->by_collated_rsa_sha1 = dc->by_rsa_sha1; +} + +/** + * Collation function for ed25519 consensuses: collate the votes for each + * entry in <b>dc</b> by ed25519 key and by RSA key. + * + * The rule is, approximately: + * If a (ed,rsa) identity is listed by more than half of authorities, + * include it. And include all (rsa)-only votes about that node as + * matching. + * + * Otherwise, if an (*,rsa) or (rsa) identity is listed by more than + * half of the authorities, and no (ed,rsa) pair for the same RSA key + * has been already been included based on the rule above, include + * that RSA identity. + */ +static void +dircollator_collate_by_ed25519(dircollator_t *dc) +{ + const int total_authorities = dc->n_authorities; + digestmap_t *rsa_digests = digestmap_new(); + + ddmap_entry_t **iter; + + /* Go over all <ed,rsa> pairs */ + HT_FOREACH(iter, double_digest_map, &dc->by_both_ids) { + ddmap_entry_t *ent = *iter; + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (ent->vrs_lst[i] != NULL) + ++n; + } + + /* If not enough authorties listed this exact <ed,rsa> pair, + * don't include it. */ + if (n <= total_authorities / 2) + continue; + + /* Now consider whether there are any other entries with the same + * RSA key (but with possibly different or missing ed value). */ + vote_routerstatus_t **vrs_lst2 = digestmap_get(dc->by_rsa_sha1, + (char*)ent->d); + tor_assert(vrs_lst2); + + for (i = 0; i < dc->n_votes; ++i) { + if (ent->vrs_lst[i] != NULL) { + ent->vrs_lst[i]->ed25519_reflects_consensus = 1; + } else if (vrs_lst2[i] && ! vrs_lst2[i]->has_ed25519_listing) { + ent->vrs_lst[i] = vrs_lst2[i]; + } + } + + /* Record that we have seen this RSA digest. */ + digestmap_set(rsa_digests, (char*)ent->d, ent->vrs_lst); + smartlist_add(dc->all_rsa_sha1_lst, ent->d); + } + + /* Now look over all entries with an RSA digest, looking for RSA digests + * we didn't put in yet. + */ + DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) { + if (digestmap_get(rsa_digests, k) != NULL) + continue; /* We already included this RSA digest */ + + int n = 0, i; + for (i = 0; i < dc->n_votes; ++i) { + if (vrs_lst[i] != NULL) + ++n; + } + + if (n <= total_authorities / 2) + continue; /* Not enough votes */ + + digestmap_set(rsa_digests, k, vrs_lst); + smartlist_add(dc->all_rsa_sha1_lst, (char *)k); + } DIGESTMAP_FOREACH_END; + + dc->by_collated_rsa_sha1 = rsa_digests; +} + +/** Return the total number of collated router entries. This function may + * only be called after dircollator_collate. */ +int +dircollator_n_routers(dircollator_t *dc) +{ + tor_assert(dc->is_collated); + return smartlist_len(dc->all_rsa_sha1_lst); +} + +/** Return an array of vote_routerstatus_t entries for the <b>idx</b>th router + * in the collation order. Each array contains n_votes elements, where the + * nth element of the array is the vote_routerstatus_t from the nth voter for + * this identity (or NULL if there is no such entry). + * + * The maximum value for <b>idx</b> is dircollator_n_routers(). + * + * This function may only be called after dircollator_collate. */ +vote_routerstatus_t ** +dircollator_get_votes_for_router(dircollator_t *dc, int idx) +{ + tor_assert(dc->is_collated); + tor_assert(idx < smartlist_len(dc->all_rsa_sha1_lst)); + return digestmap_get(dc->by_collated_rsa_sha1, + smartlist_get(dc->all_rsa_sha1_lst, idx)); +} + diff --git a/src/or/dircollate.h b/src/or/dircollate.h new file mode 100644 index 0000000000..358c730cbb --- /dev/null +++ b/src/or/dircollate.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file dircollate.h + * \brief Header file for dircollate.c. + **/ + +#ifndef TOR_DIRCOLLATE_H +#define TOR_DIRCOLLATE_H + +#include "testsupport.h" +#include "or.h" + +typedef struct dircollator_s dircollator_t; + +dircollator_t *dircollator_new(int n_votes, int n_authorities); +void dircollator_free(dircollator_t *obj); +void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v); + +void dircollator_collate(dircollator_t *dc, int consensus_method); + +int dircollator_n_routers(dircollator_t *dc); +vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc, + int idx); + +#ifdef DIRCOLLATE_PRIVATE +struct ddmap_entry_s; +typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t; +/** A dircollator keeps track of all the routerstatus entries in a + * set of networkstatus votes, and matches them by an appropriate rule. */ +struct dircollator_s { + /** True iff we have run the collation algorithm. */ + int is_collated; + /** The total number of votes that we received. */ + int n_votes; + /** The total number of authorities we acknowledge. */ + int n_authorities; + + /** The index which the next vote to be added to this collator should + * receive. */ + int next_vote_num; + /** Map from RSA-SHA1 identity digest to an array of <b>n_votes</b> + * vote_routerstatus_t* pointers, such that the i'th member of the + * array is the i'th vote's entry for that RSA-SHA1 ID.*/ + digestmap_t *by_rsa_sha1; + /** Map from <ed, RSA-SHA1> pair to an array similar to that used in + * by_rsa_sha1 above. We include <NULL,RSA-SHA1> entries for votes that + * say that there is no Ed key. */ + struct double_digest_map by_both_ids; + + /** One of two outputs created by collation: a map from RSA-SHA1 + * identity digest to an array of the vote_routerstatus_t objects. Entries + * only exist in this map for identities that we should include in the + * consensus. */ + digestmap_t *by_collated_rsa_sha1; + + /** One of two outputs created by collation: a sorted array of RSA-SHA1 + * identity digests .*/ + smartlist_t *all_rsa_sha1_lst; +}; +#endif + +#endif + diff --git a/src/or/directory.c b/src/or/directory.c index d2b6b86f6d..89b08223d2 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -1,9 +1,10 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #include "or.h" +#include "backtrace.h" #include "buffers.h" #include "circuitbuild.h" #include "config.h" @@ -23,6 +24,7 @@ #include "relay.h" #include "rendclient.h" #include "rendcommon.h" +#include "rendservice.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -81,18 +83,21 @@ static void dir_microdesc_download_failed(smartlist_t *failed, static void note_client_request(int purpose, int compressed, size_t bytes); static int client_likes_consensus(networkstatus_t *v, const char *want_url); -static void directory_initiate_command_rend(const tor_addr_t *addr, - uint16_t or_port, - uint16_t dir_port, - const char *digest, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since, - const rend_data_t *rend_query); +static void directory_initiate_command_rend( + const tor_addr_port_t *or_addr_port, + const tor_addr_port_t *dir_addr_port, + const char *digest, + uint8_t dir_purpose, + uint8_t router_purpose, + dir_indirection_t indirection, + const char *resource, + const char *payload, + size_t payload_len, + time_t if_modified_since, + const rend_data_t *rend_query); + +static void connection_dir_close_consensus_fetches( + dir_connection_t *except_this_one, const char *resource); /********* START VARIABLES **********/ @@ -142,7 +147,7 @@ purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose) /** Return a newly allocated string describing <b>auth</b>. Only describes * authority features. */ -static char * +STATIC char * authdir_type_to_string(dirinfo_type_t auth) { char *result; @@ -161,7 +166,7 @@ authdir_type_to_string(dirinfo_type_t auth) } /** Return a string describing a given directory connection purpose. */ -static const char * +STATIC const char * dir_conn_purpose_to_string(int purpose) { switch (purpose) @@ -312,7 +317,6 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, SMARTLIST_FOREACH_BEGIN(dirservers, dir_server_t *, ds) { routerstatus_t *rs = &(ds->fake_status); size_t upload_len = payload_len; - tor_addr_t ds_addr; if ((type & ds->type) == 0) continue; @@ -343,11 +347,12 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, log_info(LD_DIR, "Uploading an extrainfo too (length %d)", (int) extrainfo_len); } - tor_addr_from_ipv4h(&ds_addr, ds->addr); if (purpose_needs_anonymity(dir_purpose, router_purpose)) { indirection = DIRIND_ANONYMOUS; - } else if (!fascist_firewall_allows_address_dir(&ds_addr,ds->dir_port)) { - if (fascist_firewall_allows_address_or(&ds_addr,ds->or_port)) + } else if (!fascist_firewall_allows_dir_server(ds, + FIREWALL_DIR_CONNECTION, + 0)) { + if (fascist_firewall_allows_dir_server(ds, FIREWALL_OR_CONNECTION, 0)) indirection = DIRIND_ONEHOP; else indirection = DIRIND_ANONYMOUS; @@ -369,7 +374,7 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, /** Return true iff, according to the values in <b>options</b>, we should be * using directory guards for direct downloads of directory information. */ -static int +STATIC int should_use_directory_guards(const or_options_t *options) { /* Public (non-bridge) servers never use directory guards. */ @@ -424,14 +429,17 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags, * Use <b>pds_flags</b> as arguments to router_pick_directory_server() * or router_pick_trusteddirserver(). */ -MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose, - uint8_t router_purpose, - const char *resource, - int pds_flags)) +MOCK_IMPL(void, directory_get_from_dirserver, ( + uint8_t dir_purpose, + uint8_t router_purpose, + const char *resource, + int pds_flags, + download_want_authority_t want_authority)) { const routerstatus_t *rs = NULL; const or_options_t *options = get_options(); - int prefer_authority = directory_fetches_from_authorities(options); + int prefer_authority = (directory_fetches_from_authorities(options) + || want_authority == DL_WANT_AUTHORITY); int require_authority = 0; int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose); dirinfo_type_t type = dir_fetch_type(dir_purpose, router_purpose, resource); @@ -492,11 +500,14 @@ MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose, const node_t *node = choose_random_dirguard(type); if (node && node->ri) { /* every bridge has a routerinfo. */ - tor_addr_t addr; routerinfo_t *ri = node->ri; - node_get_addr(node, &addr); - directory_initiate_command(&addr, - ri->or_port, 0/*no dirport*/, + /* clients always make OR connections to bridges */ + tor_addr_port_t or_ap; + /* we are willing to use a non-preferred address if we need to */ + fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, + &or_ap); + directory_initiate_command(&or_ap.addr, or_ap.port, + NULL, 0, /*no dirport*/ ri->cache_info.identity_digest, dir_purpose, router_purpose, @@ -605,6 +616,95 @@ dirind_is_anon(dir_indirection_t ind) return ind == DIRIND_ANON_DIRPORT || ind == DIRIND_ANONYMOUS; } +/* Choose reachable OR and Dir addresses and ports from status, copying them + * into use_or_ap and use_dir_ap. If indirection is anonymous, then we're + * connecting via another relay, so choose the primary IPv4 address and ports. + * + * status should have at least one reachable address, if we can't choose a + * reachable address, warn and return -1. Otherwise, return 0. + */ +static int +directory_choose_address_routerstatus(const routerstatus_t *status, + dir_indirection_t indirection, + tor_addr_port_t *use_or_ap, + tor_addr_port_t *use_dir_ap) +{ + tor_assert(status != NULL); + tor_assert(use_or_ap != NULL); + tor_assert(use_dir_ap != NULL); + + const or_options_t *options = get_options(); + int have_or = 0, have_dir = 0; + + /* We expect status to have at least one reachable address if we're + * connecting to it directly. + * + * Therefore, we can simply use the other address if the one we want isn't + * allowed by the firewall. + * + * (When Tor uploads and downloads a hidden service descriptor, it uses + * DIRIND_ANONYMOUS, except for Tor2Web, which uses DIRIND_ONEHOP. + * So this code will only modify the address for Tor2Web's HS descriptor + * fetches. Even Single Onion Servers (NYI) use DIRIND_ANONYMOUS, to avoid + * HSDirs denying service by rejecting descriptors.) + */ + + /* Initialise the OR / Dir addresses */ + tor_addr_make_null(&use_or_ap->addr, AF_UNSPEC); + use_or_ap->port = 0; + tor_addr_make_null(&use_dir_ap->addr, AF_UNSPEC); + use_dir_ap->port = 0; + + /* ORPort connections */ + if (indirection == DIRIND_ANONYMOUS) { + if (status->addr) { + /* Since we're going to build a 3-hop circuit and ask the 2nd relay + * to extend to this address, always use the primary (IPv4) OR address */ + tor_addr_from_ipv4h(&use_or_ap->addr, status->addr); + use_or_ap->port = status->or_port; + have_or = 1; + } + } else if (indirection == DIRIND_ONEHOP) { + /* We use an IPv6 address if we have one and we prefer it. + * Use the preferred address and port if they are reachable, otherwise, + * use the alternate address and port (if any). + */ + have_or = fascist_firewall_choose_address_rs(status, + FIREWALL_OR_CONNECTION, 0, + use_or_ap); + } + + /* DirPort connections + * DIRIND_ONEHOP uses ORPort, but may fall back to the DirPort on relays */ + if (indirection == DIRIND_DIRECT_CONN || + indirection == DIRIND_ANON_DIRPORT || + (indirection == DIRIND_ONEHOP + && !directory_must_use_begindir(options))) { + have_dir = fascist_firewall_choose_address_rs(status, + FIREWALL_DIR_CONNECTION, 0, + use_dir_ap); + } + + /* We rejected all addresses in the relay's status. This means we can't + * connect to it. */ + if (!have_or && !have_dir) { + static int logged_backtrace = 0; + log_info(LD_BUG, "Rejected all OR and Dir addresses from %s when " + "launching an outgoing directory connection to: IPv4 %s OR %d " + "Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(status), + fmt_addr32(status->addr), status->or_port, + status->dir_port, fmt_addr(&status->ipv6_addr), + status->ipv6_orport, status->dir_port); + if (!logged_backtrace) { + log_backtrace(LOG_INFO, LD_BUG, "Addresses came from"); + logged_backtrace = 1; + } + return -1; + } + + return 0; +} + /** Same as directory_initiate_command_routerstatus(), but accepts * rendezvous data to fetch a hidden service descriptor. */ void @@ -620,8 +720,11 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, { const or_options_t *options = get_options(); const node_t *node; - tor_addr_t addr; + tor_addr_port_t use_or_ap, use_dir_ap; const int anonymized_connection = dirind_is_anon(indirection); + + tor_assert(status != NULL); + node = node_get_by_id(status->identity_digest); if (!node && anonymized_connection) { @@ -630,7 +733,6 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, routerstatus_describe(status)); return; } - tor_addr_from_ipv4h(&addr, status->addr); if (options->ExcludeNodes && options->StrictNodes && routerset_contains_routerstatus(options->ExcludeNodes, status, -1)) { @@ -642,13 +744,30 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, return; } - directory_initiate_command_rend(&addr, - status->or_port, status->dir_port, - status->identity_digest, - dir_purpose, router_purpose, - indirection, resource, - payload, payload_len, if_modified_since, - rend_query); + /* At this point, if we are a clients making a direct connection to a + * directory server, we have selected a server that has at least one address + * allowed by ClientUseIPv4/6 and Reachable{"",OR,Dir}Addresses. This + * selection uses the preference in ClientPreferIPv6{OR,Dir}Port, if + * possible. (If UseBridges is set, clients always use IPv6, and prefer it + * by default.) + * + * Now choose an address that we can use to connect to the directory server. + */ + if (directory_choose_address_routerstatus(status, indirection, &use_or_ap, + &use_dir_ap) < 0) { + return; + } + + /* We don't retry the alternate OR/Dir address for the same directory if + * the address we choose fails (#6772). + * Instead, we'll retry another directory on failure. */ + + directory_initiate_command_rend(&use_or_ap, &use_dir_ap, + status->identity_digest, + dir_purpose, router_purpose, + indirection, resource, + payload, payload_len, if_modified_since, + rend_query); } /** Launch a new connection to the directory server <b>status</b> to @@ -665,15 +784,15 @@ directory_initiate_command_routerstatus_rend(const routerstatus_t *status, * When fetching a rendezvous descriptor, <b>resource</b> is the service ID we * want to fetch. */ -void -directory_initiate_command_routerstatus(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since) +MOCK_IMPL(void, directory_initiate_command_routerstatus, + (const routerstatus_t *status, + uint8_t dir_purpose, + uint8_t router_purpose, + dir_indirection_t indirection, + const char *resource, + const char *payload, + size_t payload_len, + time_t if_modified_since)) { directory_initiate_command_routerstatus_rend(status, dir_purpose, router_purpose, @@ -711,7 +830,7 @@ connection_dir_request_failed(dir_connection_t *conn) return; /* this was a test fetch. don't retry. */ } if (!entry_list_is_constrained(get_options())) - router_set_status(conn->identity_digest, 0); /* don't try him again */ + router_set_status(conn->identity_digest, 0); /* don't try this one again */ if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) { log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from " @@ -850,6 +969,16 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status) update_certificate_downloads(time(NULL)); } +/* Should this tor instance only use begindir for all its directory requests? + */ +int +directory_must_use_begindir(const or_options_t *options) +{ + /* Clients, onion services, and bridges must use begindir, + * relays and authorities do not have to */ + return !public_server_mode(options); +} + /** Evaluate the situation and decide if we should use an encrypted * "begindir-style" connection for this directory request. * 1) If or_port is 0, or it's a direct conn and or_port is firewalled @@ -857,40 +986,90 @@ connection_dir_download_cert_failed(dir_connection_t *conn, int status) * 2) If we prefer to avoid begindir conns, and we're not fetching or * publishing a bridge relay descriptor, no. * 3) Else yes. + * If returning 0, return in *reason why we can't use begindir. + * reason must not be NULL. */ static int directory_command_should_use_begindir(const or_options_t *options, const tor_addr_t *addr, int or_port, uint8_t router_purpose, - dir_indirection_t indirection) + dir_indirection_t indirection, + const char **reason) { (void) router_purpose; - if (!or_port) + tor_assert(reason); + *reason = NULL; + + /* Reasons why we can't possibly use begindir */ + if (!or_port) { + *reason = "directory with unknown ORPort"; return 0; /* We don't know an ORPort -- no chance. */ - if (indirection == DIRIND_DIRECT_CONN || indirection == DIRIND_ANON_DIRPORT) + } + if (indirection == DIRIND_DIRECT_CONN || + indirection == DIRIND_ANON_DIRPORT) { + *reason = "DirPort connection"; return 0; - if (indirection == DIRIND_ONEHOP) - if (!fascist_firewall_allows_address_or(addr, or_port) || - directory_fetches_from_authorities(options)) - return 0; /* We're firewalled or are acting like a relay -- also no. */ + } + if (indirection == DIRIND_ONEHOP) { + /* We're firewalled and want a direct OR connection */ + if (!fascist_firewall_allows_address_addr(addr, or_port, + FIREWALL_OR_CONNECTION, 0, 0)) { + *reason = "ORPort not reachable"; + return 0; + } + } + /* Reasons why we want to avoid using begindir */ + if (indirection == DIRIND_ONEHOP) { + if (!directory_must_use_begindir(options)) { + *reason = "in relay mode"; + return 0; + } + } + /* DIRIND_ONEHOP on a client, or DIRIND_ANONYMOUS + */ + *reason = "(using begindir)"; return 1; } -/** Helper for directory_initiate_command_routerstatus: send the - * command to a server whose address is <b>address</b>, whose IP is - * <b>addr</b>, whose directory port is <b>dir_port</b>, whose tor version - * <b>supports_begindir</b>, and whose identity key digest is - * <b>digest</b>. */ +/** Helper for directory_initiate_command_rend: send the + * command to a server whose OR address/port is <b>or_addr</b>/<b>or_port</b>, + * whose directory address/port is <b>dir_addr</b>/<b>dir_port</b>, whose + * identity key digest is <b>digest</b>, with purposes <b>dir_purpose</b> and + * <b>router_purpose</b>, making an (in)direct connection as specified in + * <b>indirection</b>, with command <b>resource</b>, <b>payload</b> of + * <b>payload_len</b>, and asking for a result only <b>if_modified_since</b>. + */ void -directory_initiate_command(const tor_addr_t *_addr, - uint16_t or_port, uint16_t dir_port, +directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, + const tor_addr_t *dir_addr, uint16_t dir_port, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, dir_indirection_t indirection, const char *resource, const char *payload, size_t payload_len, time_t if_modified_since) { - directory_initiate_command_rend(_addr, or_port, dir_port, + tor_addr_port_t or_ap, dir_ap; + + /* Use the null tor_addr and 0 port if the address or port isn't valid. */ + if (tor_addr_port_is_valid(or_addr, or_port, 0)) { + tor_addr_copy(&or_ap.addr, or_addr); + or_ap.port = or_port; + } else { + /* the family doesn't matter here, so make it IPv4 */ + tor_addr_make_null(&or_ap.addr, AF_INET); + or_ap.port = or_port = 0; + } + + if (tor_addr_port_is_valid(dir_addr, dir_port, 0)) { + tor_addr_copy(&dir_ap.addr, dir_addr); + dir_ap.port = dir_port; + } else { + /* the family doesn't matter here, so make it IPv4 */ + tor_addr_make_null(&dir_ap.addr, AF_INET); + dir_ap.port = dir_port = 0; + } + + directory_initiate_command_rend(&or_ap, &dir_ap, digest, dir_purpose, router_purpose, indirection, resource, payload, payload_len, @@ -910,10 +1089,11 @@ is_sensitive_dir_purpose(uint8_t dir_purpose) } /** Same as directory_initiate_command(), but accepts rendezvous data to - * fetch a hidden service descriptor. */ + * fetch a hidden service descriptor, and takes its address & port arguments + * as tor_addr_port_t. */ static void -directory_initiate_command_rend(const tor_addr_t *_addr, - uint16_t or_port, uint16_t dir_port, +directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, + const tor_addr_port_t *dir_addr_port, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, dir_indirection_t indirection, @@ -922,19 +1102,33 @@ directory_initiate_command_rend(const tor_addr_t *_addr, time_t if_modified_since, const rend_data_t *rend_query) { + tor_assert(or_addr_port); + tor_assert(dir_addr_port); + tor_assert(or_addr_port->port || dir_addr_port->port); + tor_assert(digest); + dir_connection_t *conn; const or_options_t *options = get_options(); int socket_error = 0; - int use_begindir = directory_command_should_use_begindir(options, _addr, - or_port, router_purpose, indirection); + const char *begindir_reason = NULL; + /* Should the connection be to a relay's OR port (and inside that we will + * send our directory request)? */ + const int use_begindir = directory_command_should_use_begindir(options, + &or_addr_port->addr, or_addr_port->port, + router_purpose, indirection, + &begindir_reason); + /* Will the connection go via a three-hop Tor circuit? Note that this + * is separate from whether it will use_begindir. */ const int anonymized_connection = dirind_is_anon(indirection); - tor_addr_t addr; - - tor_assert(_addr); - tor_assert(or_port || dir_port); - tor_assert(digest); - tor_addr_copy(&addr, _addr); + /* What is the address we want to make the directory request to? If + * we're making a begindir request this is the ORPort of the relay + * we're contacting; if not a begindir request, this is its DirPort. + * Note that if anonymized_connection is true, we won't be initiating + * a connection directly to this address. */ + tor_addr_t addr; + tor_addr_copy(&addr, &(use_begindir ? or_addr_port : dir_addr_port)->addr); + uint16_t port = (use_begindir ? or_addr_port : dir_addr_port)->port; log_debug(LD_DIR, "anonymized %d, use_begindir %d.", anonymized_connection, use_begindir); @@ -948,6 +1142,14 @@ directory_initiate_command_rend(const tor_addr_t *_addr, (void)is_sensitive_dir_purpose; #endif + /* use encrypted begindir connections for everything except relays + * this provides better protection for directory fetches */ + if (!use_begindir && directory_must_use_begindir(options)) { + log_warn(LD_BUG, "Client could not use begindir connection: %s", + begindir_reason ? begindir_reason : "(NULL)"); + return; + } + /* ensure that we don't make direct connections when a SOCKS server is * configured. */ if (!anonymized_connection && !use_begindir && !options->HTTPProxy && @@ -957,11 +1159,25 @@ directory_initiate_command_rend(const tor_addr_t *_addr, return; } + /* Make sure that the destination addr and port we picked is viable. */ + if (!port || tor_addr_is_null(&addr)) { + static int logged_backtrace = 0; + log_warn(LD_DIR, + "Cannot make an outgoing %sconnection without %sPort.", + use_begindir ? "begindir " : "", + use_begindir ? "an OR" : "a Dir"); + if (!logged_backtrace) { + log_backtrace(LOG_INFO, LD_BUG, "Address came from"); + logged_backtrace = 1; + } + return; + } + conn = dir_connection_new(tor_addr_family(&addr)); /* set up conn so it's got all the data we need to remember */ tor_addr_copy(&conn->base_.addr, &addr); - conn->base_.port = use_begindir ? or_port : dir_port; + conn->base_.port = port; conn->base_.address = tor_dup_addr(&addr); memcpy(conn->identity_digest, digest, DIGEST_LEN); @@ -984,16 +1200,13 @@ directory_initiate_command_rend(const tor_addr_t *_addr, if (options->HTTPProxy) { tor_addr_copy(&addr, &options->HTTPProxyAddr); - dir_port = options->HTTPProxyPort; + port = options->HTTPProxyPort; } switch (connection_connect(TO_CONN(conn), conn->base_.address, &addr, - dir_port, &socket_error)) { + port, &socket_error)) { case -1: - connection_dir_request_failed(conn); /* retry if we want */ - /* XXX we only pass 'conn' above, not 'resource', 'payload', - * etc. So in many situations it can't retry! -RD */ - connection_free(TO_CONN(conn)); + connection_mark_for_close(TO_CONN(conn)); return; case 1: /* start flushing conn */ @@ -1008,8 +1221,12 @@ directory_initiate_command_rend(const tor_addr_t *_addr, /* writable indicates finish, readable indicates broken link, error indicates broken link in windowsland. */ } - } else { /* we want to connect via a tor connection */ + } else { + /* We will use a Tor circuit (maybe 1-hop, maybe 3-hop, maybe with + * begindir, maybe not with begindir) */ + entry_connection_t *linked_conn; + /* Anonymized tunneled connections can never share a circuit. * One-hop directory connections can share circuits with each other * but nothing else. */ @@ -1031,7 +1248,7 @@ directory_initiate_command_rend(const tor_addr_t *_addr, conn->base_.address, conn->base_.port, digest, SESSION_GROUP_DIRCONN, iso_flags, - use_begindir, conn->dirconn_direct); + use_begindir, !anonymized_connection); if (!linked_conn) { log_warn(LD_NET,"Making tunnel to dirserver failed."); connection_mark_for_close(TO_CONN(conn)); @@ -1137,6 +1354,23 @@ directory_get_consensus_url(const char *resource) return url; } +/** + * Copies the ipv6 from source to destination, subject to buffer size limit + * size. If decorate is true, makes sure the copied address is decorated. + */ +static void +copy_ipv6_address(char* destination, const char* source, size_t len, + int decorate) { + tor_assert(destination); + tor_assert(source); + + if (decorate && source[0] != '[') { + tor_snprintf(destination, len, "[%s]", source); + } else { + strlcpy(destination, source, len); + } +} + /** Queue an appropriate HTTP command on conn-\>outbuf. The other args * are as in directory_initiate_command(). */ @@ -1148,6 +1382,9 @@ directory_send_command(dir_connection_t *conn, { char proxystring[256]; char hoststring[128]; + /* NEEDS to be the same size hoststring. + Will be decorated with brackets around it if it is ipv6. */ + char decorated_address[128]; smartlist_t *headers = smartlist_new(); char *url; char request[8192]; @@ -1160,12 +1397,20 @@ directory_send_command(dir_connection_t *conn, if (resource) conn->requested_resource = tor_strdup(resource); + /* decorate the ip address if it is ipv6 */ + if (strchr(conn->base_.address, ':')) { + copy_ipv6_address(decorated_address, conn->base_.address, + sizeof(decorated_address), 1); + } else { + strlcpy(decorated_address, conn->base_.address, sizeof(decorated_address)); + } + /* come up with a string for which Host: we want */ if (conn->base_.port == 80) { - strlcpy(hoststring, conn->base_.address, sizeof(hoststring)); + strlcpy(hoststring, decorated_address, sizeof(hoststring)); } else { - tor_snprintf(hoststring, sizeof(hoststring),"%s:%d", - conn->base_.address, conn->base_.port); + tor_snprintf(hoststring, sizeof(hoststring), "%s:%d", + decorated_address, conn->base_.port); } /* Format if-modified-since */ @@ -1573,7 +1818,7 @@ load_downloaded_routers(const char *body, smartlist_t *which, added = router_load_routers_from_string(body, NULL, SAVED_NOWHERE, which, descriptor_digests, buf); - if (general) + if (added && general) control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, count_loading_descriptors_progress()); return added; @@ -1597,7 +1842,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) size_t body_len = 0, orig_len = 0; int status_code; time_t date_header = 0; - long delta; + long apparent_skew; compress_method_t compression; int plausible; int skewed = 0; @@ -1656,28 +1901,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn) * and the date header. (We used to check now-date_header, but that's * inaccurate if we spend a lot of time downloading.) */ - delta = conn->base_.timestamp_lastwritten - date_header; - if (labs(delta)>ALLOW_DIRECTORY_TIME_SKEW) { - char dbuf[64]; + apparent_skew = conn->base_.timestamp_lastwritten - date_header; + if (labs(apparent_skew)>ALLOW_DIRECTORY_TIME_SKEW) { int trusted = router_digest_is_trusted_dir(conn->identity_digest); - format_time_interval(dbuf, sizeof(dbuf), delta); - log_fn(trusted ? LOG_WARN : LOG_INFO, - LD_HTTP, - "Received directory with skewed time (server '%s:%d'): " - "It seems that our clock is %s by %s, or that theirs is %s. " - "Tor requires an accurate clock to work: please check your time, " - "timezone, and date settings.", - conn->base_.address, conn->base_.port, - delta>0 ? "ahead" : "behind", dbuf, - delta>0 ? "behind" : "ahead"); + clock_skew_warning(TO_CONN(conn), apparent_skew, trusted, LD_HTTP, + "directory", "DIRSERV"); skewed = 1; /* don't check the recommended-versions line */ - if (trusted) - control_event_general_status(LOG_WARN, - "CLOCK_SKEW SKEW=%ld SOURCE=DIRSERV:%s:%d", - delta, conn->base_.address, conn->base_.port); } else { log_debug(LD_HTTP, "Time on received directory is within tolerance; " - "we are %ld seconds skewed. (That's okay.)", delta); + "we are %ld seconds skewed. (That's okay.)", apparent_skew); } } (void) skewed; /* skewed isn't used yet. */ @@ -1783,11 +2015,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn) networkstatus_consensus_download_failed(0, flavname); return -1; } + + /* If we launched other fetches for this consensus, cancel them. */ + connection_dir_close_consensus_fetches(conn, flavname); + /* launches router downloads as needed */ routers_update_all_from_networkstatus(now, 3); update_microdescs_from_networkstatus(now); update_microdesc_downloads(now); - directory_info_has_arrived(now, 0); + directory_info_has_arrived(now, 0, 0); log_info(LD_DIR, "Successfully loaded consensus."); } @@ -1823,7 +2059,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) * ones got flushed to disk so it's safe to call this on them */ connection_dir_download_cert_failed(conn, status_code); } else { - directory_info_has_arrived(now, 0); + directory_info_has_arrived(now, 0, 0); log_info(LD_DIR, "Successfully loaded certificates from fetch."); } } else { @@ -1937,7 +2173,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (load_downloaded_routers(body, which, descriptor_digests, conn->router_purpose, conn->base_.address)) - directory_info_has_arrived(now, 0); + directory_info_has_arrived(now, 0, 0); } } if (which) { /* mark remaining ones as failed */ @@ -1988,8 +2224,11 @@ connection_dir_client_reached_eof(dir_connection_t *conn) /* Mark remaining ones as failed. */ dir_microdesc_download_failed(which, status_code); } - control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, - count_loading_descriptors_progress()); + if (mds && smartlist_len(mds)) { + control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS, + count_loading_descriptors_progress()); + directory_info_has_arrived(now, 0, 1); + } SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); smartlist_free(which); smartlist_free(mds); @@ -2102,40 +2341,57 @@ connection_dir_client_reached_eof(dir_connection_t *conn) control_event_hs_descriptor_failed(conn->rend_data, \ conn->identity_digest, \ reason) ) + #define SEND_HS_DESC_FAILED_CONTENT() ( \ + control_event_hs_descriptor_content(conn->rend_data->onion_address, \ + conn->requested_resource, \ + conn->identity_digest, \ + NULL) ) tor_assert(conn->rend_data); log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d " "(%s))", (int)body_len, status_code, escaped(reason)); switch (status_code) { case 200: - switch (rend_cache_store_v2_desc_as_client(body, - conn->requested_resource, conn->rend_data)) { - case RCS_BADDESC: - case RCS_NOTDIR: /* Impossible */ - log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. " - "Retrying at another directory."); - /* We'll retry when connection_about_to_close_connection() - * cleans this dir conn up. */ - SEND_HS_DESC_FAILED_EVENT("BAD_DESC"); - break; - case RCS_OKAY: - default: - /* success. notify pending connections about this. */ - log_info(LD_REND, "Successfully fetched v2 rendezvous " - "descriptor."); - control_event_hs_descriptor_received(conn->rend_data, - conn->identity_digest); - conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2; - rend_client_desc_trynow(conn->rend_data->onion_address); - break; + { + rend_cache_entry_t *entry = NULL; + + if (rend_cache_store_v2_desc_as_client(body, + conn->requested_resource, conn->rend_data, &entry) < 0) { + log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. " + "Retrying at another directory."); + /* We'll retry when connection_about_to_close_connection() + * cleans this dir conn up. */ + SEND_HS_DESC_FAILED_EVENT("BAD_DESC"); + SEND_HS_DESC_FAILED_CONTENT(); + } else { + char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; + /* Should never be NULL here if we found the descriptor. */ + tor_assert(entry); + rend_get_service_id(entry->parsed->pk, service_id); + + /* success. notify pending connections about this. */ + log_info(LD_REND, "Successfully fetched v2 rendezvous " + "descriptor."); + control_event_hs_descriptor_received(service_id, + conn->rend_data, + conn->identity_digest); + control_event_hs_descriptor_content(service_id, + conn->requested_resource, + conn->identity_digest, + body); + conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2; + rend_client_desc_trynow(service_id); + memwipe(service_id, 0, sizeof(service_id)); } break; + } case 404: /* Not there. We'll retry when * connection_about_to_close_connection() cleans this conn up. */ log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: " "Retrying at another directory."); SEND_HS_DESC_FAILED_EVENT("NOT_FOUND"); + SEND_HS_DESC_FAILED_CONTENT(); break; case 400: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " @@ -2143,6 +2399,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn) "v2 rendezvous query? Retrying at another directory.", escaped(reason)); SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED"); + SEND_HS_DESC_FAILED_CONTENT(); break; default: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " @@ -2152,30 +2409,44 @@ connection_dir_client_reached_eof(dir_connection_t *conn) status_code, escaped(reason), conn->base_.address, conn->base_.port); SEND_HS_DESC_FAILED_EVENT("UNEXPECTED"); + SEND_HS_DESC_FAILED_CONTENT(); break; } } if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) { + #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \ + control_event_hs_descriptor_upload_failed( \ + conn->identity_digest, \ + conn->rend_data->onion_address, \ + reason) ) log_info(LD_REND,"Uploaded rendezvous descriptor (status %d " "(%s))", status_code, escaped(reason)); + /* Without the rend data, we'll have a problem identifying what has been + * uploaded for which service. */ + tor_assert(conn->rend_data); switch (status_code) { case 200: log_info(LD_REND, "Uploading rendezvous descriptor: finished with status " "200 (%s)", escaped(reason)); + control_event_hs_descriptor_uploaded(conn->identity_digest, + conn->rend_data->onion_address); + rend_service_desc_has_uploaded(conn->rend_data); break; case 400: log_warn(LD_REND,"http status 400 (%s) response from dirserver " "'%s:%d'. Malformed rendezvous descriptor?", escaped(reason), conn->base_.address, conn->base_.port); + SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED"); break; default: log_warn(LD_REND,"http status %d (%s) response unexpected (server " "'%s:%d').", status_code, escaped(reason), conn->base_.address, conn->base_.port); + SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED"); break; } } @@ -2558,7 +2829,7 @@ client_likes_consensus(networkstatus_t *v, const char *want_url) /** Return the compression level we should use for sending a compressed * response of size <b>n_bytes</b>. */ -static zlib_compression_level_t +STATIC zlib_compression_level_t choose_compression_level(ssize_t n_bytes) { if (! have_been_under_memory_pressure()) { @@ -2579,7 +2850,7 @@ choose_compression_level(ssize_t n_bytes) * service descriptor. On finding one, write a response into * conn-\>outbuf. If the request is unrecognized, send a 400. * Always return 0. */ -static int +STATIC int directory_handle_command_get(dir_connection_t *conn, const char *headers, const char *req_body, size_t req_body_len) { @@ -2735,10 +3006,8 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, } if (1) { - struct in_addr in; tor_addr_t addr; - if (tor_inet_aton((TO_CONN(conn))->address, &in)) { - tor_addr_from_ipv4h(&addr, ntohl(in.s_addr)); + if (tor_addr_parse(&addr, (TO_CONN(conn))->address) >= 0) { geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, time(NULL)); @@ -2839,7 +3108,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, }); if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later."); + write_http_status_line(conn, 503, "Directory busy, try again later"); goto vote_done; } write_http_response_header(conn, body_len ? body_len : -1, compressed, @@ -3036,7 +3305,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, len += c->cache_info.signed_descriptor_len); if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) { - write_http_status_line(conn, 503, "Directory busy, try again later."); + write_http_status_line(conn, 503, "Directory busy, try again later"); goto keys_done; } @@ -3060,13 +3329,12 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers, goto done; } - if (options->HidServDirectoryV2 && - connection_dir_is_encrypted(conn) && + if (connection_dir_is_encrypted(conn) && !strcmpstart(url,"/tor/rendezvous2/")) { /* Handle v2 rendezvous descriptor fetch request. */ const char *descp; const char *query = url + strlen("/tor/rendezvous2/"); - if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) { + if (rend_valid_descriptor_id(query)) { log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'", safe_str(escaped(query))); switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) { @@ -3198,6 +3466,13 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, conn->base_.state = DIR_CONN_STATE_SERVER_WRITING; + if (!public_server_mode(options)) { + log_info(LD_DIR, "Rejected dir post request from %s " + "since we're not a public relay.", conn->base_.address); + write_http_status_line(conn, 503, "Not acting as a public relay"); + goto done; + } + if (parse_http_url(headers, &url) < 0) { write_http_status_line(conn, 400, "Bad request"); return 0; @@ -3205,27 +3480,16 @@ directory_handle_command_post(dir_connection_t *conn, const char *headers, log_debug(LD_DIRSERV,"rewritten url as '%s'.", escaped(url)); /* Handle v2 rendezvous service publish request. */ - if (options->HidServDirectoryV2 && - connection_dir_is_encrypted(conn) && + if (connection_dir_is_encrypted(conn) && !strcmpstart(url,"/tor/rendezvous2/publish")) { - switch (rend_cache_store_v2_desc_as_dir(body)) { - case RCS_NOTDIR: - log_info(LD_REND, "Rejected v2 rend descriptor (length %d) from %s " - "since we're not currently a hidden service directory.", - (int)body_len, conn->base_.address); - write_http_status_line(conn, 503, "Currently not acting as v2 " - "hidden service directory"); - break; - case RCS_BADDESC: - log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.", - (int)body_len, conn->base_.address); - write_http_status_line(conn, 400, - "Invalid v2 service descriptor rejected"); - break; - case RCS_OKAY: - default: - write_http_status_line(conn, 200, "Service descriptor (v2) stored"); - log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted"); + if (rend_cache_store_v2_desc_as_dir(body) < 0) { + log_warn(LD_REND, "Rejected v2 rend descriptor (length %d) from %s.", + (int)body_len, conn->base_.address); + write_http_status_line(conn, 400, + "Invalid v2 service descriptor rejected"); + } else { + write_http_status_line(conn, 200, "Service descriptor (v2) stored"); + log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted"); } goto done; } @@ -3365,7 +3629,7 @@ connection_dir_finished_flushing(dir_connection_t *conn) tor_assert(conn->base_.type == CONN_TYPE_DIR); /* Note that we have finished writing the directory response. For direct - * connections this means we're done, for tunneled connections its only + * connections this means we're done; for tunneled connections it's only * an intermediate step. */ if (conn->dirreq_id) geoip_change_dirreq_state(conn->dirreq_id, DIRREQ_TUNNELED, @@ -3406,8 +3670,38 @@ connection_dir_finished_flushing(dir_connection_t *conn) return 0; } +/* We just got a new consensus! If there are other in-progress requests + * for this consensus flavor (for example because we launched several in + * parallel), cancel them. + * + * We do this check here (not just in + * connection_ap_handshake_attach_circuit()) to handle the edge case where + * a consensus fetch begins and ends before some other one tries to attach to + * a circuit, in which case the other one won't know that we're all happy now. + * + * Don't mark the conn that just gave us the consensus -- otherwise we + * would end up double-marking it when it cleans itself up. + */ +static void +connection_dir_close_consensus_fetches(dir_connection_t *except_this_one, + const char *resource) +{ + smartlist_t *conns_to_close = + connection_dir_list_by_purpose_and_resource(DIR_PURPOSE_FETCH_CONSENSUS, + resource); + SMARTLIST_FOREACH_BEGIN(conns_to_close, dir_connection_t *, d) { + if (d == except_this_one) + continue; + log_info(LD_DIR, "Closing consensus fetch (to %s) since one " + "has just arrived.", TO_CONN(d)->address); + connection_mark_for_close(TO_CONN(d)); + } SMARTLIST_FOREACH_END(d); + smartlist_free(conns_to_close); +} + /** Connected handler for directory connections: begin sending data to the - * server */ + * server, and return 0. + * Only used when connections don't immediately connect. */ int connection_dir_finished_connecting(dir_connection_t *conn) { @@ -3418,84 +3712,229 @@ connection_dir_finished_connecting(dir_connection_t *conn) log_debug(LD_HTTP,"Dir connection to router %s:%u established.", conn->base_.address,conn->base_.port); - conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; /* start flushing conn */ + /* start flushing conn */ + conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; return 0; } /** Decide which download schedule we want to use based on descriptor type - * in <b>dls</b> and whether we are acting as directory <b>server</b>, and - * then return a list of int pointers defining download delays in seconds. - * Helper function for download_status_increment_failure() and - * download_status_reset(). */ -static const smartlist_t * -find_dl_schedule_and_len(download_status_t *dls, int server) + * in <b>dls</b> and <b>options</b>. + * Then return a list of int pointers defining download delays in seconds. + * Helper function for download_status_increment_failure(), + * download_status_reset(), and download_status_increment_attempt(). */ +STATIC const smartlist_t * +find_dl_schedule(download_status_t *dls, const or_options_t *options) { + const int dir_server = dir_server_mode(options); + const int multi_d = networkstatus_consensus_can_use_multiple_directories( + options); + const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( + time(NULL)); + const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks( + options); switch (dls->schedule) { case DL_SCHED_GENERIC: - if (server) - return get_options()->TestingServerDownloadSchedule; - else - return get_options()->TestingClientDownloadSchedule; + if (dir_server) { + return options->TestingServerDownloadSchedule; + } else { + return options->TestingClientDownloadSchedule; + } case DL_SCHED_CONSENSUS: - if (server) - return get_options()->TestingServerConsensusDownloadSchedule; - else - return get_options()->TestingClientConsensusDownloadSchedule; + if (!multi_d) { + return options->TestingServerConsensusDownloadSchedule; + } else { + if (we_are_bootstrapping) { + if (!use_fallbacks) { + /* A bootstrapping client without extra fallback directories */ + return + options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule; + } else if (dls->want_authority) { + /* A bootstrapping client with extra fallback directories, but + * connecting to an authority */ + return + options->ClientBootstrapConsensusAuthorityDownloadSchedule; + } else { + /* A bootstrapping client connecting to extra fallback directories + */ + return + options->ClientBootstrapConsensusFallbackDownloadSchedule; + } + } else { + return options->TestingClientConsensusDownloadSchedule; + } + } case DL_SCHED_BRIDGE: - return get_options()->TestingBridgeDownloadSchedule; + return options->TestingBridgeDownloadSchedule; default: tor_assert(0); } + + /* Impossible, but gcc will fail with -Werror without a `return`. */ + return NULL; +} + +/* Find the current delay for dls based on schedule. + * Set dls->next_attempt_at based on now, and return the delay. + * Helper for download_status_increment_failure and + * download_status_increment_attempt. */ +STATIC int +download_status_schedule_get_delay(download_status_t *dls, + const smartlist_t *schedule, + time_t now) +{ + tor_assert(dls); + tor_assert(schedule); + + int delay = INT_MAX; + uint8_t dls_schedule_position = (dls->increment_on + == DL_SCHED_INCREMENT_ATTEMPT + ? dls->n_download_attempts + : dls->n_download_failures); + + if (dls_schedule_position < smartlist_len(schedule)) + delay = *(int *)smartlist_get(schedule, dls_schedule_position); + else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD) + delay = INT_MAX; + else + delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); + + /* A negative delay makes no sense. Knowing that delay is + * non-negative allows us to safely do the wrapping check below. */ + tor_assert(delay >= 0); + + /* Avoid now+delay overflowing INT_MAX, by comparing with a subtraction + * that won't overflow (since delay is non-negative). */ + if (delay < INT_MAX && now <= INT_MAX - delay) { + dls->next_attempt_at = now+delay; + } else { + dls->next_attempt_at = TIME_MAX; + } + + return delay; +} + +/* Log a debug message about item, which increments on increment_action, has + * incremented dls_n_download_increments times. The message varies based on + * was_schedule_incremented (if not, not_incremented_response is logged), and + * the values of increment, dls_next_attempt_at, and now. + * Helper for download_status_increment_failure and + * download_status_increment_attempt. */ +static void +download_status_log_helper(const char *item, int was_schedule_incremented, + const char *increment_action, + const char *not_incremented_response, + uint8_t dls_n_download_increments, int increment, + time_t dls_next_attempt_at, time_t now) +{ + if (item) { + if (!was_schedule_incremented) + log_debug(LD_DIR, "%s %s %d time(s); I'll try again %s.", + item, increment_action, (int)dls_n_download_increments, + not_incremented_response); + else if (increment == 0) + log_debug(LD_DIR, "%s %s %d time(s); I'll try again immediately.", + item, increment_action, (int)dls_n_download_increments); + else if (dls_next_attempt_at < TIME_MAX) + log_debug(LD_DIR, "%s %s %d time(s); I'll try again in %d seconds.", + item, increment_action, (int)dls_n_download_increments, + (int)(dls_next_attempt_at-now)); + else + log_debug(LD_DIR, "%s %s %d time(s); Giving up for a while.", + item, increment_action, (int)dls_n_download_increments); + } } -/** Called when an attempt to download <b>dls</b> has failed with HTTP status +/** Determine when a failed download attempt should be retried. + * Called when an attempt to download <b>dls</b> has failed with HTTP status * <b>status_code</b>. Increment the failure count (if the code indicates a - * real failure) and set <b>dls</b>-\>next_attempt_at to an appropriate time - * in the future. */ + * real failure, or if we're a server) and set <b>dls</b>-\>next_attempt_at to + * an appropriate time in the future and return it. + * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_ATTEMPT, increment the + * failure count, and return a time in the far future for the next attempt (to + * avoid an immediate retry). */ time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now) { - const smartlist_t *schedule; - int increment; + int increment = -1; tor_assert(dls); + + /* only count the failure if it's permanent, or we're a server */ if (status_code != 503 || server) { if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) ++dls->n_download_failures; } - schedule = find_dl_schedule_and_len(dls, server); + if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) { + /* We don't find out that a failure-based schedule has attempted a + * connection until that connection fails. + * We'll never find out about successful connections, but this doesn't + * matter, because schedules are reset after a successful download. + */ + if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1) + ++dls->n_download_attempts; - if (dls->n_download_failures < smartlist_len(schedule)) - increment = *(int *)smartlist_get(schedule, dls->n_download_failures); - else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD) - increment = INT_MAX; - else - increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); + /* only return a failure retry time if this schedule increments on failures + */ + const smartlist_t *schedule = find_dl_schedule(dls, get_options()); + increment = download_status_schedule_get_delay(dls, schedule, now); + } - if (increment < INT_MAX) - dls->next_attempt_at = now+increment; - else - dls->next_attempt_at = TIME_MAX; + download_status_log_helper(item, !dls->increment_on, "failed", + "concurrently", dls->n_download_failures, + increment, dls->next_attempt_at, now); - if (item) { - if (increment == 0) - log_debug(LD_DIR, "%s failed %d time(s); I'll try again immediately.", - item, (int)dls->n_download_failures); - else if (dls->next_attempt_at < TIME_MAX) - log_debug(LD_DIR, "%s failed %d time(s); I'll try again in %d seconds.", - item, (int)dls->n_download_failures, - (int)(dls->next_attempt_at-now)); - else - log_debug(LD_DIR, "%s failed %d time(s); Giving up for a while.", - item, (int)dls->n_download_failures); + if (dls->increment_on == DL_SCHED_INCREMENT_ATTEMPT) { + /* stop this schedule retrying on failure, it will launch concurrent + * connections instead */ + return TIME_MAX; + } else { + return dls->next_attempt_at; } +} + +/** Determine when the next download attempt should be made when using an + * attempt-based (potentially concurrent) download schedule. + * Called when an attempt to download <b>dls</b> is being initiated. + * Increment the attempt count and set <b>dls</b>-\>next_attempt_at to an + * appropriate time in the future and return it. + * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_FAILURE, don't increment + * the attempts, and return a time in the far future (to avoid launching a + * concurrent attempt). */ +time_t +download_status_increment_attempt(download_status_t *dls, const char *item, + time_t now) +{ + int delay = -1; + tor_assert(dls); + + if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) { + /* this schedule should retry on failure, and not launch any concurrent + attempts */ + log_info(LD_BUG, "Tried to launch an attempt-based connection on a " + "failure-based schedule."); + return TIME_MAX; + } + + if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1) + ++dls->n_download_attempts; + + const smartlist_t *schedule = find_dl_schedule(dls, get_options()); + delay = download_status_schedule_get_delay(dls, schedule, now); + + download_status_log_helper(item, dls->increment_on, "attempted", + "on failure", dls->n_download_attempts, + delay, dls->next_attempt_at, now); + return dls->next_attempt_at; } /** Reset <b>dls</b> so that it will be considered downloadable * immediately, and/or to show that we don't need it anymore. * + * Must be called to initialise a download schedule, otherwise the zeroth item + * in the schedule will never be used. + * * (We find the zeroth element of the download schedule, and set * next_attempt_at to be the appropriate offset from 'now'. In most * cases this means setting it to 'now', so the item will be immediately @@ -3504,14 +3943,16 @@ download_status_increment_failure(download_status_t *dls, int status_code, void download_status_reset(download_status_t *dls) { - if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD) + if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD + || dls->n_download_attempts == IMPOSSIBLE_TO_DOWNLOAD) return; /* Don't reset this. */ - const smartlist_t *schedule = find_dl_schedule_and_len( - dls, get_options()->DirPort_set); + const smartlist_t *schedule = find_dl_schedule(dls, get_options()); dls->n_download_failures = 0; + dls->n_download_attempts = 0; dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0); + /* Don't reset dls->want_authority or dls->increment_on */ } /** Return the number of failures on <b>dls</b> since the last success (if @@ -3522,6 +3963,22 @@ download_status_get_n_failures(const download_status_t *dls) return dls->n_download_failures; } +/** Return the number of attempts to download <b>dls</b> since the last success + * (if any). This can differ from download_status_get_n_failures() due to + * outstanding concurrent attempts. */ +int +download_status_get_n_attempts(const download_status_t *dls) +{ + return dls->n_download_attempts; +} + +/** Return the next time to attempt to download <b>dls</b>. */ +time_t +download_status_get_next_attempt_at(const download_status_t *dls) +{ + return dls->next_attempt_at; +} + /** Called when one or more routerdesc (or extrainfo, if <b>was_extrainfo</b>) * fetches have failed (with uppercase fingerprints listed in <b>failed</b>, * either as descriptor digests or as identity digests based on diff --git a/src/or/directory.h b/src/or/directory.h index 4899eb5c8c..7646cac03f 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -16,18 +16,20 @@ int directories_have_accepted_server_descriptor(void); void directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, dirinfo_type_t type, const char *payload, size_t payload_len, size_t extrainfo_len); -MOCK_DECL(void, directory_get_from_dirserver, (uint8_t dir_purpose, - uint8_t router_purpose, - const char *resource, - int pds_flags)); +MOCK_DECL(void, directory_get_from_dirserver, ( + uint8_t dir_purpose, + uint8_t router_purpose, + const char *resource, + int pds_flags, + download_want_authority_t want_authority)); void directory_get_from_all_authorities(uint8_t dir_purpose, uint8_t router_purpose, const char *resource); /** Enumeration of ways to connect to a directory server */ typedef enum { - /** Default: connect over a one-hop Tor circuit but fall back to direct - * connection */ + /** Default: connect over a one-hop Tor circuit. Relays fall back to direct + * DirPort connections, clients, onion services, and bridges do not */ DIRIND_ONEHOP=0, /** Connect over a multi-hop anonymizing Tor circuit */ DIRIND_ANONYMOUS=1, @@ -37,14 +39,18 @@ typedef enum { DIRIND_ANON_DIRPORT, } dir_indirection_t; -void directory_initiate_command_routerstatus(const routerstatus_t *status, - uint8_t dir_purpose, - uint8_t router_purpose, - dir_indirection_t indirection, - const char *resource, - const char *payload, - size_t payload_len, - time_t if_modified_since); +int directory_must_use_begindir(const or_options_t *options); + +MOCK_DECL(void, directory_initiate_command_routerstatus, + (const routerstatus_t *status, + uint8_t dir_purpose, + uint8_t router_purpose, + dir_indirection_t indirection, + const char *resource, + const char *payload, + size_t payload_len, + time_t if_modified_since)); + void directory_initiate_command_routerstatus_rend(const routerstatus_t *status, uint8_t dir_purpose, uint8_t router_purpose, @@ -64,8 +70,8 @@ int connection_dir_process_inbuf(dir_connection_t *conn); int connection_dir_finished_flushing(dir_connection_t *conn); int connection_dir_finished_connecting(dir_connection_t *conn); void connection_dir_about_to_close(dir_connection_t *dir_conn); -void directory_initiate_command(const tor_addr_t *addr, - uint16_t or_port, uint16_t dir_port, +void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, + const tor_addr_t *dir_addr, uint16_t dir_port, const char *digest, uint8_t dir_purpose, uint8_t router_purpose, dir_indirection_t indirection, @@ -90,34 +96,41 @@ int router_supports_extrainfo(const char *identity_digest, int is_authority); time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now); +time_t download_status_increment_attempt(download_status_t *dls, + const char *item, time_t now); /** Increment the failure count of the download_status_t <b>dls</b>, with * the optional status code <b>sc</b>. */ #define download_status_failed(dls, sc) \ download_status_increment_failure((dls), (sc), NULL, \ - get_options()->DirPort_set, time(NULL)) + dir_server_mode(get_options()), \ + time(NULL)) void download_status_reset(download_status_t *dls); static int download_status_is_ready(download_status_t *dls, time_t now, int max_failures); /** Return true iff, as of <b>now</b>, the resource tracked by <b>dls</b> is * ready to get its download reattempted. */ -static INLINE int +static inline int download_status_is_ready(download_status_t *dls, time_t now, int max_failures) { - return (dls->n_download_failures <= max_failures - && dls->next_attempt_at <= now); + int under_failure_limit = (dls->n_download_failures <= max_failures + && dls->n_download_attempts <= max_failures); + return (under_failure_limit && dls->next_attempt_at <= now); } static void download_status_mark_impossible(download_status_t *dl); /** Mark <b>dl</b> as never downloadable. */ -static INLINE void +static inline void download_status_mark_impossible(download_status_t *dl) { dl->n_download_failures = IMPOSSIBLE_TO_DOWNLOAD; + dl->n_download_attempts = IMPOSSIBLE_TO_DOWNLOAD; } int download_status_get_n_failures(const download_status_t *dls); +int download_status_get_n_attempts(const download_status_t *dls); +time_t download_status_get_next_attempt_at(const download_status_t *dls); #ifdef TOR_UNIT_TESTS /* Used only by directory.c and test_dir.c */ @@ -127,6 +140,20 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose); STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose, const char *resource); +STATIC int directory_handle_command_get(dir_connection_t *conn, + const char *headers, + const char *req_body, + size_t req_body_len); +STATIC int download_status_schedule_get_delay(download_status_t *dls, + const smartlist_t *schedule, + time_t now); + +STATIC char* authdir_type_to_string(dirinfo_type_t auth); +STATIC const char * dir_conn_purpose_to_string(int purpose); +STATIC int should_use_directory_guards(const or_options_t *options); +STATIC zlib_compression_level_t choose_compression_level(ssize_t n_bytes); +STATIC const smartlist_t *find_dl_schedule(download_status_t *dls, + const or_options_t *options); #endif #endif diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 65bfafba6c..dafaed8bf2 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define DIRSERV_PRIVATE @@ -18,6 +18,7 @@ #include "dirserv.h" #include "dirvote.h" #include "hibernate.h" +#include "keypin.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" @@ -27,6 +28,7 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" +#include "torcert.h" /** * \file dirserv.c @@ -60,7 +62,7 @@ static uint32_t dirserv_get_status_impl(const char *fp, const char *nickname, uint32_t addr, uint16_t or_port, const char *platform, const char **msg, - int should_log); + int severity); static void clear_cached_dir(cached_dir_t *d); static const signed_descriptor_t *get_signed_descriptor_by_fp( const char *fp, @@ -225,6 +227,16 @@ dirserv_load_fingerprint_file(void) return 0; } +/* If this is set, then we don't allow routers that have advertised an Ed25519 + * identity to stop doing so. This is going to be essential for good identity + * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could + * just sign fake descriptors missing the Ed25519 key. But we won't actually + * be able to prevent that kind of thing until we're confident that there + * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have + * to leave this #undef. + */ +#undef DISABLE_DISABLING_ED25519 + /** Check whether <b>router</b> has a nickname/identity key combination that * we recognize from the fingerprint list, or an IP we automatically act on * according to our configuration. Return the appropriate router status. @@ -232,9 +244,11 @@ dirserv_load_fingerprint_file(void) * If the status is 'FP_REJECT' and <b>msg</b> is provided, set * *<b>msg</b> to an explanation of why. */ uint32_t -dirserv_router_get_status(const routerinfo_t *router, const char **msg) +dirserv_router_get_status(const routerinfo_t *router, const char **msg, + int severity) { char d[DIGEST_LEN]; + const int key_pinning = get_options()->AuthDirPinKeys; if (crypto_pk_get_digest(router->identity_pkey, d)) { log_warn(LD_BUG,"Error computing fingerprint"); @@ -243,9 +257,45 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg) return FP_REJECT; } + if (router->cache_info.signing_key_cert) { + /* This has an ed25519 identity key. */ + if (KEYPIN_MISMATCH == + keypin_check((const uint8_t*)router->cache_info.identity_digest, + router->cache_info.signing_key_cert->signing_key.pubkey)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s has an Ed25519 key, " + "but the <rsa,ed25519> keys don't match what they were before.", + router_describe(router)); + if (key_pinning) { + if (msg) { + *msg = "Ed25519 identity key or RSA identity key has changed."; + } + return FP_REJECT; + } + } + } else { + /* No ed25519 key */ + if (KEYPIN_MISMATCH == keypin_check_lone_rsa( + (const uint8_t*)router->cache_info.identity_digest)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s has no Ed25519 key, " + "when we previously knew an Ed25519 for it. Ignoring for now, " + "since Ed25519 keys are fairly new.", + router_describe(router)); +#ifdef DISABLE_DISABLING_ED25519 + if (key_pinning) { + if (msg) { + *msg = "Ed25519 identity key has disappeared."; + } + return FP_REJECT; + } +#endif + } + } + return dirserv_get_status_impl(d, router->nickname, router->addr, router->or_port, - router->platform, msg, 1); + router->platform, msg, severity); } /** Return true if there is no point in downloading the router described by @@ -257,7 +307,7 @@ dirserv_would_reject_router(const routerstatus_t *rs) res = dirserv_get_status_impl(rs->identity_digest, rs->nickname, rs->addr, rs->or_port, - NULL, NULL, 0); + NULL, NULL, LOG_DEBUG); return (res & FP_REJECT) != 0; } @@ -266,13 +316,13 @@ dirserv_would_reject_router(const routerstatus_t *rs) * (hex, no spaces), nickname, address (used for logging only), IP address, OR * port and platform (logging only) as arguments. * - * If should_log is false, do not log messages. (There's not much point in + * Log messages at 'severity'. (There's not much point in * logging that we're rejecting servers we'll not download.) */ static uint32_t dirserv_get_status_impl(const char *id_digest, const char *nickname, uint32_t addr, uint16_t or_port, - const char *platform, const char **msg, int should_log) + const char *platform, const char **msg, int severity) { uint32_t result = 0; router_status_t *status_by_digest; @@ -280,10 +330,9 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, if (!fingerprint_list) fingerprint_list = authdir_config_new(); - if (should_log) - log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.", - strmap_size(fingerprint_list->fp_by_name), - digestmap_size(fingerprint_list->status_by_digest)); + log_debug(LD_DIRSERV, "%d fingerprints, %d digests known.", + strmap_size(fingerprint_list->fp_by_name), + digestmap_size(fingerprint_list->status_by_digest)); /* Versions before Tor 0.2.4.18-rc are too old to support, and are * missing some important security fixes too. Disable them. */ @@ -308,23 +357,22 @@ dirserv_get_status_impl(const char *id_digest, const char *nickname, } if (authdir_policy_badexit_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Marking '%s' as bad exit because of address '%s'", + log_fn(severity, LD_DIRSERV, + "Marking '%s' as bad exit because of address '%s'", nickname, fmt_addr32(addr)); result |= FP_BADEXIT; } if (!authdir_policy_permits_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Rejecting '%s' because of address '%s'", + log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'", nickname, fmt_addr32(addr)); if (msg) *msg = "Authdir is rejecting routers in this range."; return FP_REJECT; } if (!authdir_policy_valid_address(addr, or_port)) { - if (should_log) - log_info(LD_DIRSERV, "Not marking '%s' valid because of address '%s'", + log_fn(severity, LD_DIRSERV, + "Not marking '%s' valid because of address '%s'", nickname, fmt_addr32(addr)); result |= FP_INVALID; } @@ -380,9 +428,9 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int complain, int *valid_out) { /* Okay. Now check whether the fingerprint is recognized. */ - uint32_t status = dirserv_router_get_status(ri, msg); time_t now; int severity = (complain && ri->contact_info) ? LOG_NOTICE : LOG_INFO; + uint32_t status = dirserv_router_get_status(ri, msg, severity); tor_assert(msg); if (status & FP_REJECT) return -1; /* msg is already set. */ @@ -539,6 +587,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) char *desc, *nickname; const size_t desclen = ri->cache_info.signed_descriptor_len + ri->cache_info.annotations_len; + const int key_pinning = get_options()->AuthDirPinKeys; *msg = NULL; /* If it's too big, refuse it now. Otherwise we'll cache it all over the @@ -578,6 +627,29 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) return ROUTER_IS_ALREADY_KNOWN; } + /* Do keypinning again ... this time, to add the pin if appropriate */ + int keypin_status; + if (ri->cache_info.signing_key_cert) { + keypin_status = keypin_check_and_add( + (const uint8_t*)ri->cache_info.identity_digest, + ri->cache_info.signing_key_cert->signing_key.pubkey, + ! key_pinning); + } else { + keypin_status = keypin_check_lone_rsa( + (const uint8_t*)ri->cache_info.identity_digest); +#ifndef DISABLE_DISABLING_ED25519 + if (keypin_status == KEYPIN_MISMATCH) + keypin_status = KEYPIN_NOT_FOUND; +#endif + } + if (keypin_status == KEYPIN_MISMATCH && key_pinning) { + log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because " + "its key did not match an older RSA/Ed25519 keypair", + router_describe(ri), source); + *msg = "Looks like your keypair does not match its older value."; + return ROUTER_AUTHDIR_REJECTS; + } + /* Make a copy of desc, since router_add_to_routerlist might free * ri and its associated signed_descriptor_t. */ desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen); @@ -619,12 +691,14 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source) static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, const char **msg) { - const routerinfo_t *ri; + routerinfo_t *ri; int r; tor_assert(msg); *msg = NULL; - ri = router_get_by_id_digest(ei->cache_info.identity_digest); + /* Needs to be mutable so routerinfo_incompatible_with_extrainfo + * can mess with some of the flags in ri->cache_info. */ + ri = router_get_mutable_by_digest(ei->cache_info.identity_digest); if (!ri) { *msg = "No corresponding router descriptor for extra-info descriptor"; extrainfo_free(ei); @@ -644,7 +718,8 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg) return ROUTER_BAD_EI; } - if ((r = routerinfo_incompatible_with_extrainfo(ri, ei, NULL, msg))) { + if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, + &ri->cache_info, msg))) { extrainfo_free(ei); return r < 0 ? ROUTER_IS_ALREADY_KNOWN : ROUTER_BAD_EI; } @@ -670,7 +745,7 @@ directory_remove_invalid(void) uint32_t r; if (!ent) continue; - r = dirserv_router_get_status(ent, &msg); + r = dirserv_router_get_status(ent, &msg, LOG_INFO); router_get_description(description, ent); if (r & FP_REJECT) { log_info(LD_DIRSERV, "Router %s is now rejected: %s", @@ -725,7 +800,7 @@ list_single_server_status(const routerinfo_t *desc, int is_live) } /* DOCDOC running_long_enough_to_decide_unreachable */ -static INLINE int +static inline int running_long_enough_to_decide_unreachable(void) { return time_of_process_start @@ -1019,13 +1094,13 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); - if (!options->DirPort_set && !refuseunknown) + if (!dir_server_mode(options) && !refuseunknown) return 0; if (!server_mode(options) || !advertised_server_mode()) return 0; me = router_get_my_routerinfo(); - if (!me || (!me->dir_port && !refuseunknown)) - return 0; /* if dirport not advertised, return 0 too */ + if (!me || (!me->supports_tunnelled_dir_requests && !refuseunknown)) + return 0; /* if we don't service directory requests, return 0 too */ return 1; } @@ -1056,16 +1131,19 @@ directory_fetches_dir_info_later(const or_options_t *options) int directory_caches_unknown_auth_certs(const or_options_t *options) { - return options->DirPort_set || options->BridgeRelay; + return dir_server_mode(options) || options->BridgeRelay; } -/** Return 1 if we want to keep descriptors, networkstatuses, etc around - * and we're willing to serve them to others. Else return 0. +/** Return 1 if we want to keep descriptors, networkstatuses, etc around. + * Else return 0. + * Check options->DirPort_set and directory_permits_begindir_requests() + * to see if we are willing to serve these directory documents to others via + * the DirPort and begindir-over-ORPort, respectively. */ int directory_caches_dir_info(const or_options_t *options) { - if (options->BridgeRelay || options->DirPort_set) + if (options->BridgeRelay || dir_server_mode(options)) return 1; if (!server_mode(options) || !advertised_server_mode()) return 0; @@ -1081,7 +1159,7 @@ directory_caches_dir_info(const or_options_t *options) int directory_permits_begindir_requests(const or_options_t *options) { - return options->BridgeRelay != 0 || options->DirPort_set; + return options->BridgeRelay != 0 || dir_server_mode(options); } /** Return 1 if we have no need to fetch new descriptors. This generally @@ -1158,7 +1236,7 @@ free_cached_dir_(void *_d) void dirserv_set_cached_consensus_networkstatus(const char *networkstatus, const char *flavor_name, - const digests_t *digests, + const common_digests_t *digests, time_t published) { cached_dir_t *new_networkstatus; @@ -1167,7 +1245,7 @@ dirserv_set_cached_consensus_networkstatus(const char *networkstatus, cached_consensuses = strmap_new(); new_networkstatus = new_cached_dir(tor_strdup(networkstatus), published); - memcpy(&new_networkstatus->digests, digests, sizeof(digests_t)); + memcpy(&new_networkstatus->digests, digests, sizeof(common_digests_t)); old_networkstatus = strmap_set(cached_consensuses, flavor_name, new_networkstatus); if (old_networkstatus) @@ -1230,7 +1308,7 @@ static uint32_t guard_bandwidth_excluding_exits_kb = 0; /** Helper: estimate the uptime of a router given its stated uptime and the * amount of time since it last stated its stated uptime. */ -static INLINE long +static inline long real_uptime(const routerinfo_t *router, time_t now) { if (now < router->cache_info.published_on) @@ -1278,8 +1356,9 @@ dirserv_thinks_router_is_unreliable(time_t now, } /** Return true iff <b>router</b> should be assigned the "HSDir" flag. + * * Right now this means it advertises support for it, it has a high uptime, - * it has a DirPort open, it has the Stable flag and it's currently + * it's a directory cache, it has the Stable and Fast flags, and it's currently * considered Running. * * This function needs to be called after router-\>is_running has @@ -1306,8 +1385,9 @@ dirserv_thinks_router_is_hs_dir(const routerinfo_t *router, else uptime = real_uptime(router, now); - return (router->wants_to_be_hs_dir && router->dir_port && - node->is_stable && + return (router->wants_to_be_hs_dir && + router->supports_tunnelled_dir_requests && + node->is_stable && node->is_fast && uptime >= get_options()->MinUptimeHidServDirectoryV2 && router_is_active(router, node, now)); } @@ -1350,13 +1430,13 @@ router_counts_toward_thresholds(const node_t *node, time_t now, * * Also, set the is_exit flag of each router appropriately. */ static void -dirserv_compute_performance_thresholds(routerlist_t *rl, - digestmap_t *omit_as_sybil) +dirserv_compute_performance_thresholds(digestmap_t *omit_as_sybil) { int n_active, n_active_nonexit, n_familiar; uint32_t *uptimes, *bandwidths_kb, *bandwidths_excluding_exits_kb; long *tks; double *mtbfs, *wfus; + smartlist_t *nodelist; time_t now = time(NULL); const or_options_t *options = get_options(); @@ -1374,27 +1454,28 @@ dirserv_compute_performance_thresholds(routerlist_t *rl, guard_tk = 0; guard_wfu = 0; + nodelist_assert_ok(); + nodelist = nodelist_get_list(); + /* Initialize arrays that will hold values for each router. We'll * sort them and use that to compute thresholds. */ n_active = n_active_nonexit = 0; /* Uptime for every active router. */ - uptimes = tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t)); + uptimes = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t)); /* Bandwidth for every active router. */ - bandwidths_kb = tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t)); + bandwidths_kb = tor_calloc(smartlist_len(nodelist), sizeof(uint32_t)); /* Bandwidth for every active non-exit router. */ bandwidths_excluding_exits_kb = - tor_calloc(smartlist_len(rl->routers), sizeof(uint32_t)); + tor_calloc(smartlist_len(nodelist), sizeof(uint32_t)); /* Weighted mean time between failure for each active router. */ - mtbfs = tor_calloc(smartlist_len(rl->routers), sizeof(double)); + mtbfs = tor_calloc(smartlist_len(nodelist), sizeof(double)); /* Time-known for each active router. */ - tks = tor_calloc(smartlist_len(rl->routers), sizeof(long)); + tks = tor_calloc(smartlist_len(nodelist), sizeof(long)); /* Weighted fractional uptime for each active router. */ - wfus = tor_calloc(smartlist_len(rl->routers), sizeof(double)); - - nodelist_assert_ok(); + wfus = tor_calloc(smartlist_len(nodelist), sizeof(double)); /* Now, fill in the arrays. */ - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { + SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) { if (options->BridgeAuthoritativeDir && node->ri && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) @@ -1470,7 +1551,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl, * fill wfus with the wfu of every such "familiar" router. */ n_familiar = 0; - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { + SMARTLIST_FOREACH_BEGIN(nodelist, node_t *, node) { if (router_counts_toward_thresholds(node, now, omit_as_sybil, require_mbw)) { routerinfo_t *ri = node->ri; @@ -1524,11 +1605,10 @@ dirserv_compute_performance_thresholds(routerlist_t *rl, * networkstatus_getinfo_by_purpose(). */ void -dirserv_compute_bridge_flag_thresholds(routerlist_t *rl) +dirserv_compute_bridge_flag_thresholds(void) { - digestmap_t *omit_as_sybil = digestmap_new(); - dirserv_compute_performance_thresholds(rl, omit_as_sybil); + dirserv_compute_performance_thresholds(omit_as_sybil); digestmap_free(omit_as_sybil, NULL); } @@ -1681,16 +1761,13 @@ dirserv_get_bandwidth_for_router_kb(const routerinfo_t *ri) * how many measured bandwidths we know. This is used to decide whether we * ever trust advertised bandwidths for purposes of assigning flags. */ static void -dirserv_count_measured_bws(routerlist_t *rl) +dirserv_count_measured_bws(const smartlist_t *routers) { /* Initialize this first */ routers_with_measured_bw = 0; - tor_assert(rl); - tor_assert(rl->routers); - /* Iterate over the routerlist and count measured bandwidths */ - SMARTLIST_FOREACH_BEGIN(rl->routers, routerinfo_t *, ri) { + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { /* Check if we know a measured bandwidth for this one */ if (dirserv_has_measured_bw(ri->cache_info.identity_digest)) { ++routers_with_measured_bw; @@ -1849,7 +1926,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, rs->is_hs_dir?" HSDir":"", rs->is_flagged_running?" Running":"", rs->is_stable?" Stable":"", - (rs->dir_port!=0)?" V2Dir":"", + rs->is_v2_dir?" V2Dir":"", rs->is_valid?" Valid":""); /* length of "opt v \n" */ @@ -1931,6 +2008,16 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, smartlist_add_asprintf(chunks, "p %s\n", summary); tor_free(summary); } + + if (format == NS_V3_VOTE && vrs) { + if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { + smartlist_add(chunks, tor_strdup("id ed25519 none\n")); + } else { + char ed_b64[BASE64_DIGEST256_LEN+1]; + digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id); + smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64); + } + } } done: @@ -2044,6 +2131,50 @@ get_possible_sybil_list(const smartlist_t *routers) return omit_as_sybil; } +/** If there are entries in <b>routers</b> with exactly the same ed25519 keys, + * remove the older one. If they are exactly the same age, remove the one + * with the greater descriptor digest. May alter the order of the list. */ +static void +routers_make_ed_keys_unique(smartlist_t *routers) +{ + routerinfo_t *ri2; + digest256map_t *by_ed_key = digest256map_new(); + + SMARTLIST_FOREACH_BEGIN(routers, routerinfo_t *, ri) { + ri->omit_from_vote = 0; + if (ri->cache_info.signing_key_cert == NULL) + continue; /* No ed key */ + const uint8_t *pk = ri->cache_info.signing_key_cert->signing_key.pubkey; + if ((ri2 = digest256map_get(by_ed_key, pk))) { + /* Duplicate; must omit one. Set the omit_from_vote flag in whichever + * one has the earlier published_on. */ + const time_t ri_pub = ri->cache_info.published_on; + const time_t ri2_pub = ri2->cache_info.published_on; + if (ri2_pub < ri_pub || + (ri2_pub == ri_pub && + memcmp(ri->cache_info.signed_descriptor_digest, + ri2->cache_info.signed_descriptor_digest,DIGEST_LEN)<0)) { + digest256map_set(by_ed_key, pk, ri); + ri2->omit_from_vote = 1; + } else { + ri->omit_from_vote = 1; + } + } else { + /* Add to map */ + digest256map_set(by_ed_key, pk, ri); + } + } SMARTLIST_FOREACH_END(ri); + + digest256map_free(by_ed_key, NULL); + + /* Now remove every router where the omit_from_vote flag got set. */ + SMARTLIST_FOREACH_BEGIN(routers, const routerinfo_t *, ri) { + if (ri->omit_from_vote) { + SMARTLIST_DEL_CURRENT(routers, ri); + } + } SMARTLIST_FOREACH_END(ri); +} + /** Extract status information from <b>ri</b> and from other authority * functions and store it in <b>rs</b>>. * @@ -2055,8 +2186,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, node_t *node, routerinfo_t *ri, time_t now, - int listbadexits, - int vote_on_hsdirs) + int listbadexits) { const or_options_t *options = get_options(); uint32_t routerbw_kb = dirserv_get_credible_bandwidth_kb(ri); @@ -2069,10 +2199,8 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, /* Already set by compute_performance_thresholds. */ rs->is_exit = node->is_exit; rs->is_stable = node->is_stable = - router_is_active(ri, node, now) && !dirserv_thinks_router_is_unreliable(now, ri, 1, 0); rs->is_fast = node->is_fast = - router_is_active(ri, node, now) && !dirserv_thinks_router_is_unreliable(now, ri, 0, 1); rs->is_flagged_running = node->is_running; /* computed above */ @@ -2093,8 +2221,8 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, } rs->is_bad_exit = listbadexits && node->is_bad_exit; - node->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, node, now); - rs->is_hs_dir = vote_on_hsdirs && node->is_hs_dir; + rs->is_hs_dir = node->is_hs_dir = + dirserv_thinks_router_is_hs_dir(ri, node, now); rs->is_named = rs->is_unnamed = 0; @@ -2106,6 +2234,7 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); rs->or_port = ri->or_port; rs->dir_port = ri->dir_port; + rs->is_v2_dir = ri->supports_tunnelled_dir_requests; if (options->AuthDirHasIPv6Connectivity == 1 && !tor_addr_is_null(&ri->ipv6_addr) && node->last_reachable6 >= now - REACHABLE_TIMEOUT) { @@ -2115,26 +2244,41 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, rs->ipv6_orport = ri->ipv6_orport; } - /* Iff we are in a testing network, use TestingDirAuthVoteExit, - TestingDirAuthVoteGuard, and TestingDirAuthVoteHSDir to - give out the Exit, Guard, and HSDir flags, respectively. - But don't set the corresponding node flags. */ if (options->TestingTorNetwork) { - if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, - rs, 0)) { - rs->is_exit = 1; - } + dirserv_set_routerstatus_testing(rs); + } +} - if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, - rs, 0)) { - rs->is_possible_guard = 1; - } +/** Use TestingDirAuthVoteExit, TestingDirAuthVoteGuard, and + * TestingDirAuthVoteHSDir to give out the Exit, Guard, and HSDir flags, + * respectively. But don't set the corresponding node flags. + * Should only be called if TestingTorNetwork is set. */ +STATIC void +dirserv_set_routerstatus_testing(routerstatus_t *rs) +{ + const or_options_t *options = get_options(); - if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir, - rs, 0)) { - /* TestingDirAuthVoteHSDir respects VoteOnHidServDirectoriesV2 */ - rs->is_hs_dir = vote_on_hsdirs; - } + tor_assert(options->TestingTorNetwork); + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteExit, + rs, 0)) { + rs->is_exit = 1; + } else if (options->TestingDirAuthVoteExitIsStrict) { + rs->is_exit = 0; + } + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteGuard, + rs, 0)) { + rs->is_possible_guard = 1; + } else if (options->TestingDirAuthVoteGuardIsStrict) { + rs->is_possible_guard = 0; + } + + if (routerset_contains_routerstatus(options->TestingDirAuthVoteHSDir, + rs, 0)) { + rs->is_hs_dir = 1; + } else if (options->TestingDirAuthVoteHSDirIsStrict) { + rs->is_hs_dir = 0; } } @@ -2659,7 +2803,6 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, char identity_digest[DIGEST_LEN]; char signing_key_digest[DIGEST_LEN]; int listbadexits = options->AuthDirListBadExits; - int vote_on_hsdirs = options->VoteOnHidServDirectoriesV2; routerlist_t *rl = router_get_routerlist(); time_t now = time(NULL); time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; @@ -2722,6 +2865,8 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, routers = smartlist_new(); smartlist_add_all(routers, rl->routers); + routers_make_ed_keys_unique(routers); + /* After this point, don't use rl->routers; use 'routers' instead. */ routers_sort_by_identity(routers); omit_as_sybil = get_possible_sybil_list(routers); @@ -2732,9 +2877,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* Count how many have measured bandwidths so we know how to assign flags; * this must come before dirserv_compute_performance_thresholds() */ - dirserv_count_measured_bws(rl); + dirserv_count_measured_bws(routers); - dirserv_compute_performance_thresholds(rl, omit_as_sybil); + dirserv_compute_performance_thresholds(omit_as_sybil); routerstatuses = smartlist_new(); microdescriptors = smartlist_new(); @@ -2750,8 +2895,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, vrs = tor_malloc_zero(sizeof(vote_routerstatus_t)); rs = &vrs->status; set_routerstatus_from_routerinfo(rs, node, ri, now, - listbadexits, - vote_on_hsdirs); + listbadexits); + + if (ri->cache_info.signing_key_cert) { + memcpy(vrs->ed25519_id, + ri->cache_info.signing_key_cert->signing_key.pubkey, + ED25519_PUBKEY_LEN); + } if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) clear_status_flags_on_sybil(rs); @@ -2843,14 +2993,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, v3_out->known_flags = smartlist_new(); smartlist_split_string(v3_out->known_flags, - "Authority Exit Fast Guard Stable V2Dir Valid", + "Authority Exit Fast Guard Stable V2Dir Valid HSDir", 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (vote_on_reachability) smartlist_add(v3_out->known_flags, tor_strdup("Running")); if (listbadexits) smartlist_add(v3_out->known_flags, tor_strdup("BadExit")); - if (vote_on_hsdirs) - smartlist_add(v3_out->known_flags, tor_strdup("HSDir")); smartlist_sort_strings(v3_out->known_flags); if (options->ConsensusParams) { @@ -3005,7 +3153,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, DSR_HEX|DSR_SORT_UNIQ); SMARTLIST_FOREACH_BEGIN(digests, const char *, d) { if (router_digest_is_me(d)) { - /* make sure desc_routerinfo exists */ + /* calling router_get_my_routerinfo() to make sure it exists */ const routerinfo_t *ri = router_get_my_routerinfo(); if (ri) smartlist_add(descs_out, (void*) &(ri->cache_info)); @@ -3037,7 +3185,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, * router listening at <b>address</b>:<b>or_port</b>, and has yielded * a certificate with digest <b>digest_rcvd</b>. * - * Inform the reachability checker that we could get to this guy. + * Inform the reachability checker that we could get to this relay. */ void dirserv_orconn_tls_done(const tor_addr_t *addr, @@ -3657,7 +3805,9 @@ validate_recommended_package_line(const char *line) cp = end_of_word + 1; } - return (n_entries == 0) ? 0 : 1; + /* If we reach this point, we have at least 1 entry. */ + tor_assert(n_entries > 0); + return 1; } /** Release all storage used by the directory server. */ diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 311a513dbe..9a9725ad6f 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -50,7 +50,7 @@ int list_server_status_v1(smartlist_t *routers, char **router_status_out, int dirserv_dump_directory_to_string(char **dir_out, crypto_pk_t *private_key); char *dirserv_get_flag_thresholds_line(void); -void dirserv_compute_bridge_flag_thresholds(routerlist_t *rl); +void dirserv_compute_bridge_flag_thresholds(void); int directory_fetches_from_authorities(const or_options_t *options); int directory_fetches_dir_info_early(const or_options_t *options); @@ -63,9 +63,9 @@ int directory_too_idle_to_fetch_descriptors(const or_options_t *options, cached_dir_t *dirserv_get_consensus(const char *flavor_name); void dirserv_set_cached_consensus_networkstatus(const char *consensus, - const char *flavor_name, - const digests_t *digests, - time_t published); + const char *flavor_name, + const common_digests_t *digests, + time_t published); void dirserv_clear_old_networkstatuses(time_t cutoff); int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, const char **msg, @@ -84,7 +84,8 @@ int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int complain, int *valid_out); uint32_t dirserv_router_get_status(const routerinfo_t *router, - const char **msg); + const char **msg, + int severity); void dirserv_set_node_flags_from_authoritative_status(node_t *node, uint32_t authstatus); @@ -108,6 +109,8 @@ int validate_recommended_package_line(const char *line); #ifdef DIRSERV_PRIVATE +STATIC void dirserv_set_routerstatus_testing(routerstatus_t *rs); + /* Put the MAX_MEASUREMENT_AGE #define here so unit tests can see it */ #define MAX_MEASUREMENT_AGE (3*24*60*60) /* 3 days */ diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 7a5154dae5..62f85877fe 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -1,11 +1,12 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define DIRVOTE_PRIVATE #include "or.h" #include "config.h" +#include "dircollate.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" @@ -17,6 +18,7 @@ #include "routerlist.h" #include "routerparse.h" #include "entrynodes.h" /* needed for guardfraction methods */ +#include "torcert.h" /** * \file dirvote.c @@ -52,7 +54,6 @@ static int dirvote_perform_vote(void); static void dirvote_clear_votes(int all_votes); static int dirvote_compute_consensuses(void); static int dirvote_publish_consensus(void); -static char *make_consensus_method_list(int low, int high, const char *sep); /* ===== * Voting @@ -476,7 +477,7 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method, if (microdesc_digest256_out) { smartlist_t *digests = smartlist_new(); - const char *best_microdesc_digest; + const uint8_t *best_microdesc_digest; SMARTLIST_FOREACH_BEGIN(votes, vote_routerstatus_t *, rs) { char d[DIGEST256_LEN]; if (compare_vote_rs(rs, most)) @@ -556,13 +557,20 @@ compute_consensus_method(smartlist_t *votes) static int consensus_method_is_supported(int method) { + if (method == MIN_METHOD_FOR_ED25519_ID_IN_MD) { + /* This method was broken due to buggy code accidently left in + * dircollate.c; do not actually use it. + */ + return 0; + } + return (method >= MIN_SUPPORTED_CONSENSUS_METHOD) && (method <= MAX_SUPPORTED_CONSENSUS_METHOD); } /** Return a newly allocated string holding the numbers between low and high * (inclusive) that are supported consensus methods. */ -static char * +STATIC char * make_consensus_method_list(int low, int high, const char *separator) { char *list; @@ -1138,8 +1146,10 @@ networkstatus_compute_consensus(smartlist_t *votes, char *params = NULL; char *packages = NULL; int added_weights = 0; + dircollator_t *collator = NULL; tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); tor_assert(total_authorities >= smartlist_len(votes)); + tor_assert(total_authorities > 0); flavor_name = networkstatus_get_flavor_name(flavor); @@ -1231,6 +1241,9 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_free(combined_server_versions); smartlist_free(combined_client_versions); + if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING) + smartlist_add(flags, tor_strdup("NoEdConsensus")); + smartlist_sort_strings(flags); smartlist_uniq_strings(flags); @@ -1493,12 +1506,24 @@ networkstatus_compute_consensus(smartlist_t *votes, } ); + /* Populate the collator */ + collator = dircollator_new(smartlist_len(votes), total_authorities); + SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { + dircollator_add_vote(collator, v); + } SMARTLIST_FOREACH_END(v); + + dircollator_collate(collator, consensus_method); + /* Now go through all the votes */ flag_counts = tor_calloc(smartlist_len(flags), sizeof(int)); - while (1) { + const int num_routers = dircollator_n_routers(collator); + for (i = 0; i < num_routers; ++i) { + vote_routerstatus_t **vrs_lst = + dircollator_get_votes_for_router(collator, i); + vote_routerstatus_t *rs; routerstatus_t rs_out; - const char *lowest_id = NULL; + const char *current_rsa_id = NULL; const char *chosen_version; const char *chosen_name = NULL; int exitsummary_disagreement = 0; @@ -1506,23 +1531,9 @@ networkstatus_compute_consensus(smartlist_t *votes, int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; - int i; char microdesc_digest[DIGEST256_LEN]; tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0}; - /* Of the next-to-be-considered digest in each voter, which is first? */ - SMARTLIST_FOREACH(votes, networkstatus_t *, v, { - if (index[v_sl_idx] < size[v_sl_idx]) { - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (!lowest_id || - fast_memcmp(rs->status.identity_digest, - lowest_id, DIGEST_LEN) < 0) - lowest_id = rs->status.identity_digest; - } - }); - if (!lowest_id) /* we're out of routers. */ - break; - memset(flag_counts, 0, sizeof(int)*smartlist_len(flags)); smartlist_clear(matching_descs); smartlist_clear(chosen_flags); @@ -1530,31 +1541,29 @@ networkstatus_compute_consensus(smartlist_t *votes, num_bandwidths = 0; num_mbws = 0; num_guardfraction_inputs = 0; + int ed_consensus = 0; + const uint8_t *ed_consensus_val = NULL; /* Okay, go through all the entries for this digest. */ - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) { - if (index[v_sl_idx] >= size[v_sl_idx]) - continue; /* out of entries. */ - rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); - if (fast_memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN)) - continue; /* doesn't include this router. */ - /* At this point, we know that we're looking at a routerstatus with - * identity "lowest". - */ - ++index[v_sl_idx]; + for (int voter_idx = 0; voter_idx < smartlist_len(votes); ++voter_idx) { + if (vrs_lst[voter_idx] == NULL) + continue; /* This voter had nothing to say about this entry. */ + rs = vrs_lst[voter_idx]; ++n_listing; + current_rsa_id = rs->status.identity_digest; + smartlist_add(matching_descs, rs); if (rs->version && rs->version[0]) smartlist_add(versions, rs->version); /* Tally up all the flags. */ - for (i = 0; i < n_voter_flags[v_sl_idx]; ++i) { - if (rs->flags & (U64_LITERAL(1) << i)) - ++flag_counts[flag_map[v_sl_idx][i]]; + for (int flag = 0; flag < n_voter_flags[voter_idx]; ++flag) { + if (rs->flags & (U64_LITERAL(1) << flag)) + ++flag_counts[flag_map[voter_idx][flag]]; } - if (named_flag[v_sl_idx] >= 0 && - (rs->flags & (U64_LITERAL(1) << named_flag[v_sl_idx]))) { + if (named_flag[voter_idx] >= 0 && + (rs->flags & (U64_LITERAL(1) << named_flag[voter_idx]))) { if (chosen_name && strcmp(chosen_name, rs->status.nickname)) { log_notice(LD_DIR, "Conflict on naming for router: %s vs %s", chosen_name, rs->status.nickname); @@ -1575,13 +1584,36 @@ networkstatus_compute_consensus(smartlist_t *votes, if (rs->status.has_bandwidth) bandwidths_kb[num_bandwidths++] = rs->status.bandwidth_kb; - } SMARTLIST_FOREACH_END(v); + + /* Count number for which ed25519 is canonical. */ + if (rs->ed25519_reflects_consensus) { + ++ed_consensus; + if (ed_consensus_val) { + tor_assert(fast_memeq(ed_consensus_val, rs->ed25519_id, + ED25519_PUBKEY_LEN)); + } else { + ed_consensus_val = rs->ed25519_id; + } + } + } /* We don't include this router at all unless more than half of * the authorities we believe in list it. */ if (n_listing <= total_authorities/2) continue; + if (ed_consensus > 0) { + tor_assert(consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING); + if (ed_consensus <= total_authorities / 2) { + log_warn(LD_BUG, "Not enough entries had ed_consensus set; how " + "can we have a consensus of %d?", ed_consensus); + } + } + + /* The clangalyzer can't figure out that this will never be NULL + * if n_listing is at least 1 */ + tor_assert(current_rsa_id); + /* Figure out the most popular opinion of what the most recent * routerinfo and its contents are. */ memset(microdesc_digest, 0, sizeof(microdesc_digest)); @@ -1589,8 +1621,9 @@ networkstatus_compute_consensus(smartlist_t *votes, microdesc_digest, &alt_orport); /* Copy bits of that into rs_out. */ memset(&rs_out, 0, sizeof(rs_out)); - tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN)); - memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN); + tor_assert(fast_memeq(current_rsa_id, + rs->status.identity_digest,DIGEST_LEN)); + memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, DIGEST_LEN); rs_out.addr = rs->status.addr; @@ -1614,7 +1647,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *d = strmap_get_lc(name_to_id_map, rs_out.nickname); if (!d) { is_named = is_unnamed = 0; - } else if (fast_memeq(d, lowest_id, DIGEST_LEN)) { + } else if (fast_memeq(d, current_rsa_id, DIGEST_LEN)) { is_named = 1; is_unnamed = 0; } else { is_named = 0; is_unnamed = 1; @@ -1630,6 +1663,10 @@ networkstatus_compute_consensus(smartlist_t *votes, } else if (!strcmp(fl, "Unnamed")) { if (is_unnamed) smartlist_add(chosen_flags, (char*)fl); + } else if (!strcmp(fl, "NoEdConsensus") && + consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING) { + if (ed_consensus <= total_authorities/2) + smartlist_add(chosen_flags, (char*)fl); } else { if (flag_counts[fl_sl_idx] > n_flag_voters[fl_sl_idx]/2) { smartlist_add(chosen_flags, (char*)fl); @@ -1980,6 +2017,7 @@ networkstatus_compute_consensus(smartlist_t *votes, done: + dircollator_free(collator); tor_free(client_versions); tor_free(server_versions); tor_free(packages); @@ -2111,14 +2149,14 @@ networkstatus_add_detached_signatures(networkstatus_t *target, /** Make sure all the digests we know match, and at least one matches. */ { - digests_t *digests = strmap_get(sigs->digests, flavor); + common_digests_t *digests = strmap_get(sigs->digests, flavor); int n_matches = 0; int alg; if (!digests) { *msg_out = "No digests for given consensus flavor"; return -1; } - for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) { + for (alg = DIGEST_SHA1; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) { if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) { if (fast_memeq(target->digests.d[alg], digests->d[alg], DIGEST256_LEN)) { @@ -2244,7 +2282,8 @@ networkstatus_format_signatures(networkstatus_t *consensus, for_detached_signatures ? flavor_name : "", digest_name, id, sk); } - base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len); + base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len, + BASE64_ENCODE_MULTILINE); strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf)); smartlist_add(elements, tor_strdup(buf)); } SMARTLIST_FOREACH_END(sig); @@ -2310,7 +2349,7 @@ networkstatus_get_detached_signatures(smartlist_t *consensuses) /* start with SHA256; we don't include SHA1 for anything but the basic * consensus. */ - for (alg = DIGEST_SHA256; alg < N_DIGEST_ALGORITHMS; ++alg) { + for (alg = DIGEST_SHA256; alg < N_COMMON_DIGEST_ALGORITHMS; ++alg) { char d[HEX_DIGEST256_LEN+1]; const char *alg_name = crypto_digest_algorithm_get_name(alg); @@ -3368,8 +3407,8 @@ dirvote_free_all(void) * ==== */ /** Return the body of the consensus that we're currently trying to build. */ -const char * -dirvote_get_pending_consensus(consensus_flavor_t flav) +MOCK_IMPL(const char *, +dirvote_get_pending_consensus, (consensus_flavor_t flav)) { tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS); return pending_consensuses[flav].body; @@ -3377,8 +3416,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav) /** Return the signatures that we know for the consensus that we're currently * trying to build. */ -const char * -dirvote_get_pending_detached_signatures(void) +MOCK_IMPL(const char *, +dirvote_get_pending_detached_signatures, (void)) { return pending_consensus_signatures; } @@ -3459,7 +3498,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) char kbuf[128]; base64_encode(kbuf, sizeof(kbuf), (const char*)ri->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN); + CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } @@ -3486,9 +3525,19 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) } if (consensus_method >= MIN_METHOD_FOR_ID_HASH_IN_MD) { - char idbuf[BASE64_DIGEST_LEN+1]; - digest_to_base64(idbuf, ri->cache_info.identity_digest); - smartlist_add_asprintf(chunks, "id rsa1024 %s\n", idbuf); + char idbuf[ED25519_BASE64_LEN+1]; + const char *keytype; + if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_IN_MD && + ri->cache_info.signing_key_cert && + ri->cache_info.signing_key_cert->signing_key_included) { + keytype = "ed25519"; + ed25519_public_to_base64(idbuf, + &ri->cache_info.signing_key_cert->signing_key); + } else { + keytype = "rsa1024"; + digest_to_base64(idbuf, ri->cache_info.identity_digest); + } + smartlist_add_asprintf(chunks, "id %s %s\n", keytype, idbuf); } output = smartlist_join_strings(chunks, "", 0, NULL); @@ -3561,7 +3610,8 @@ static const struct consensus_method_range_t { {MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1}, {MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1}, {MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1}, - {MIN_METHOD_FOR_ID_HASH_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, + {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1}, + {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/or/dirvote.h b/src/or/dirvote.h index 542563b708..0b1d284060 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -55,7 +55,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 20 +#define MAX_SUPPORTED_CONSENSUS_METHOD 22 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -86,6 +86,14 @@ * GuardFraction information in microdescriptors. */ #define MIN_METHOD_FOR_GUARDFRACTION 20 +/** Lowest consensus method where authorities may include an "id" line for + * ed25519 identities in microdescriptors. (Broken; see + * consensus_method_is_supported() for more info.) */ +#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21 +/** Lowest consensus method where authorities vote on ed25519 ids and ensure + * ed25519 id consistency. */ +#define MIN_METHOD_FOR_ED25519_ID_VOTING 22 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ @@ -129,8 +137,10 @@ int dirvote_add_signatures(const char *detached_signatures_body, const char **msg_out); /* Item access */ -const char *dirvote_get_pending_consensus(consensus_flavor_t flav); -const char *dirvote_get_pending_detached_signatures(void); +MOCK_DECL(const char*, dirvote_get_pending_consensus, + (consensus_flavor_t flav)); +MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void)); + #define DGV_BY_ID 1 #define DGV_INCLUDE_PENDING 2 #define DGV_INCLUDE_PREVIOUS 4 @@ -138,8 +148,7 @@ const cached_dir_t *dirvote_get_vote(const char *fp, int flags); void set_routerstatus_from_routerinfo(routerstatus_t *rs, node_t *node, routerinfo_t *ri, time_t now, - int listbadexits, - int vote_on_hsdirs); + int listbadexits); networkstatus_t * dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, authority_cert_t *cert); @@ -169,6 +178,7 @@ STATIC char *format_networkstatus_vote(crypto_pk_t *private_key, STATIC char *dirvote_compute_params(smartlist_t *votes, int method, int total_authorities); STATIC char *compute_consensus_package_lines(smartlist_t *votes); +STATIC char *make_consensus_method_list(int low, int high, const char *sep); #endif #endif diff --git a/src/or/dns.c b/src/or/dns.c index cc4a169422..c7adfbc971 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -11,6 +11,8 @@ * be nonblocking.) **/ +#define DNS_PRIVATE + #include "or.h" #include "circuitlist.h" #include "circuituse.h" @@ -24,7 +26,7 @@ #include "relay.h" #include "router.h" #include "ht.h" -#include "../common/sandbox.h" +#include "sandbox.h" #ifdef HAVE_EVENT2_DNS_H #include <event2/event.h> #include <event2/dns.h> @@ -81,9 +83,6 @@ struct evdns_request; #endif -/** Longest hostname we're willing to resolve. */ -#define MAX_ADDRESSLEN 256 - /** How long will we wait for an answer from the resolver before we decide * that the resolver is wedged? */ #define RESOLVE_MAX_TIMEOUT 300 @@ -102,107 +101,15 @@ static char *resolv_conf_fname = NULL; * the nameservers? Used to check whether we need to reconfigure. */ static time_t resolv_conf_mtime = 0; -/** Linked list of connections waiting for a DNS answer. */ -typedef struct pending_connection_t { - edge_connection_t *conn; - struct pending_connection_t *next; -} pending_connection_t; - -/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad - * pointers and memory stomping. */ -#define CACHED_RESOLVE_MAGIC 0x1234F00D - -/* Possible states for a cached resolve_t */ -/** We are waiting for the resolver system to tell us an answer here. - * When we get one, or when we time out, the state of this cached_resolve_t - * will become "DONE" and we'll possibly add a CACHED - * entry. This cached_resolve_t will be in the hash table so that we will - * know not to launch more requests for this addr, but rather to add more - * connections to the pending list for the addr. */ -#define CACHE_STATE_PENDING 0 -/** This used to be a pending cached_resolve_t, and we got an answer for it. - * Now we're waiting for this cached_resolve_t to expire. This should - * have no pending connections, and should not appear in the hash table. */ -#define CACHE_STATE_DONE 1 -/** We are caching an answer for this address. This should have no pending - * connections, and should appear in the hash table. */ -#define CACHE_STATE_CACHED 2 - -/** @name status values for a single DNS request. - * - * @{ */ -/** The DNS request is in progress. */ -#define RES_STATUS_INFLIGHT 1 -/** The DNS request finished and gave an answer */ -#define RES_STATUS_DONE_OK 2 -/** The DNS request finished and gave an error */ -#define RES_STATUS_DONE_ERR 3 -/**@}*/ - -/** A DNS request: possibly completed, possibly pending; cached_resolve - * structs are stored at the OR side in a hash table, and as a linked - * list from oldest to newest. - */ -typedef struct cached_resolve_t { - HT_ENTRY(cached_resolve_t) node; - uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */ - char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ - - union { - uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful. - * (In host order.) */ - int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ - } result_ipv4; /**< Outcome of IPv4 lookup */ - union { - struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if - * successful */ - int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */ - } result_ipv6; /**< Outcome of IPv6 lookup, if any */ - union { - char *hostname; /** A hostname, if PTR lookup happened successfully*/ - int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */ - } result_ptr; - /** @name Status fields - * - * These take one of the RES_STATUS_* values, depending on the state - * of the corresponding lookup. - * - * @{ */ - unsigned int res_status_ipv4 : 2; - unsigned int res_status_ipv6 : 2; - unsigned int res_status_hostname : 2; - /**@}*/ - uint8_t state; /**< Is this cached entry pending/done/informative? */ - - time_t expire; /**< Remove items from cache after this time. */ - uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */ - uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */ - /** Connections that want to know when we get an answer for this resolve. */ - pending_connection_t *pending_connections; - /** Position of this element in the heap*/ - int minheap_idx; -} cached_resolve_t; - static void purge_expired_resolves(time_t now); static void dns_found_answer(const char *address, uint8_t query_type, int dns_answer, const tor_addr_t *addr, const char *hostname, uint32_t ttl); -static void send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolve); -static int launch_resolve(cached_resolve_t *resolve); static void add_wildcarded_test_address(const char *address); static int configure_nameservers(int force); static int answer_is_wildcarded(const char *ip); -static int dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, - or_circuit_t *oncirc, char **resolved_to_hostname, - int *made_connection_pending_out, - cached_resolve_t **resolve_out); -static int set_exitconn_info_from_resolve(edge_connection_t *exitconn, - const cached_resolve_t *resolve, - char **hostname_out); static int evdns_err_is_transient(int err); static void inform_pending_connections(cached_resolve_t *resolve); static void make_pending_resolve_cached(cached_resolve_t *cached); @@ -227,7 +134,7 @@ static int dns_is_broken_for_ipv6 = 0; /** Function to compare hashed resolves on their addresses; used to * implement hash tables. */ -static INLINE int +static inline int cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b) { /* make this smarter one day? */ @@ -236,7 +143,7 @@ cached_resolves_eq(cached_resolve_t *a, cached_resolve_t *b) } /** Hash function for cached_resolve objects */ -static INLINE unsigned int +static inline unsigned int cached_resolve_hash(cached_resolve_t *a) { return (unsigned) siphash24g((const uint8_t*)a->address, strlen(a->address)); @@ -367,7 +274,7 @@ dns_clip_ttl(uint32_t ttl) /** Helper: Given a TTL from a DNS response, determine how long to hold it in * our cache. */ -static uint32_t +STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl) { if (ttl < MIN_DNS_TTL) @@ -605,9 +512,9 @@ purge_expired_resolves(time_t now) * answer back along circ; otherwise, send the answer back along * <b>conn</b>'s attached circuit. */ -static void -send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, - const cached_resolve_t *resolved) +MOCK_IMPL(STATIC void, +send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type, + const cached_resolve_t *resolved)) { char buf[RELAY_PAYLOAD_SIZE], *cp = buf; size_t buflen = 0; @@ -671,8 +578,9 @@ send_resolved_cell(edge_connection_t *conn, uint8_t answer_type, * answer back along circ; otherwise, send the answer back along * <b>conn</b>'s attached circuit. */ -static void -send_resolved_hostname_cell(edge_connection_t *conn, const char *hostname) +MOCK_IMPL(STATIC void, +send_resolved_hostname_cell,(edge_connection_t *conn, + const char *hostname)) { char buf[RELAY_PAYLOAD_SIZE]; size_t buflen; @@ -800,11 +708,11 @@ dns_resolve(edge_connection_t *exitconn) * * Set *<b>resolve_out</b> to a cached resolve, if we found one. */ -static int -dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, +MOCK_IMPL(STATIC int, +dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve, or_circuit_t *oncirc, char **hostname_out, int *made_connection_pending_out, - cached_resolve_t **resolve_out) + cached_resolve_t **resolve_out)) { cached_resolve_t *resolve; cached_resolve_t search; @@ -947,10 +855,10 @@ dns_resolve_impl(edge_connection_t *exitconn, int is_resolve, * Return -2 on a transient error, -1 on a permenent error, and 1 on * a successful lookup. */ -static int -set_exitconn_info_from_resolve(edge_connection_t *exitconn, - const cached_resolve_t *resolve, - char **hostname_out) +MOCK_IMPL(STATIC int, +set_exitconn_info_from_resolve,(edge_connection_t *exitconn, + const cached_resolve_t *resolve, + char **hostname_out)) { int ipv4_ok, ipv6_ok, answer_with_ipv4, r; uint32_t begincell_flags; @@ -1145,8 +1053,8 @@ connection_dns_remove(edge_connection_t *conn) * the resolve for <b>address</b> itself, and remove any cached results for * <b>address</b> from the cache. */ -void -dns_cancel_pending_resolve(const char *address) +MOCK_IMPL(void, +dns_cancel_pending_resolve,(const char *address)) { pending_connection_t *pend; cached_resolve_t search; @@ -1218,7 +1126,7 @@ dns_cancel_pending_resolve(const char *address) /** Return true iff <b>address</b> is one of the addresses we use to verify * that well-known sites aren't being hijacked by our DNS servers. */ -static INLINE int +static inline int is_test_address(const char *address) { const or_options_t *options = get_options(); @@ -1752,8 +1660,8 @@ launch_one_resolve(const char *address, uint8_t query_type, /** For eventdns: start resolving as necessary to find the target for * <b>exitconn</b>. Returns -1 on error, -2 on transient error, * 0 on "resolve launched." */ -static int -launch_resolve(cached_resolve_t *resolve) +MOCK_IMPL(STATIC int, +launch_resolve,(cached_resolve_t *resolve)) { tor_addr_t a; int r; @@ -2206,5 +2114,18 @@ assert_cache_ok_(void) } }); } + #endif +cached_resolve_t +*dns_get_cache_entry(cached_resolve_t *query) +{ + return HT_FIND(cache_map, &cache_root, query); +} + +void +dns_insert_cache_entry(cached_resolve_t *new_entry) +{ + HT_INSERT(cache_map, &cache_root, new_entry); +} + diff --git a/src/or/dns.h b/src/or/dns.h index b13ab0f890..b14f7dd29c 100644 --- a/src/or/dns.h +++ b/src/or/dns.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -20,7 +20,7 @@ int dns_reset(void); void connection_dns_remove(edge_connection_t *conn); void assert_connection_edge_not_dns_pending(edge_connection_t *conn); void assert_all_pending_dns_resolves_ok(void); -void dns_cancel_pending_resolve(const char *question); +MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question)); int dns_resolve(edge_connection_t *exitconn); void dns_launch_correctness_checks(void); int dns_seems_to_be_broken(void); @@ -28,5 +28,33 @@ int dns_seems_to_be_broken_for_ipv6(void); void dns_reset_correctness_checks(void); void dump_dns_mem_usage(int severity); +#ifdef DNS_PRIVATE +#include "dns_structs.h" + +STATIC uint32_t dns_get_expiry_ttl(uint32_t ttl); + +MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn, +int is_resolve,or_circuit_t *oncirc, char **hostname_out, +int *made_connection_pending_out, cached_resolve_t **resolve_out)); + +MOCK_DECL(STATIC void,send_resolved_cell,(edge_connection_t *conn, +uint8_t answer_type,const cached_resolve_t *resolved)); + +MOCK_DECL(STATIC void,send_resolved_hostname_cell,(edge_connection_t *conn, +const char *hostname)); + +cached_resolve_t *dns_get_cache_entry(cached_resolve_t *query); +void dns_insert_cache_entry(cached_resolve_t *new_entry); + +MOCK_DECL(STATIC int, +set_exitconn_info_from_resolve,(edge_connection_t *exitconn, + const cached_resolve_t *resolve, + char **hostname_out)); + +MOCK_DECL(STATIC int, +launch_resolve,(cached_resolve_t *resolve)); + +#endif + #endif diff --git a/src/or/dns_structs.h b/src/or/dns_structs.h new file mode 100644 index 0000000000..bb67459d7b --- /dev/null +++ b/src/or/dns_structs.h @@ -0,0 +1,90 @@ +#ifndef TOR_DNS_STRUCTS_H +#define TOR_DNS_STRUCTS_H + +/** Longest hostname we're willing to resolve. */ +#define MAX_ADDRESSLEN 256 + +/** Linked list of connections waiting for a DNS answer. */ +typedef struct pending_connection_t { + edge_connection_t *conn; + struct pending_connection_t *next; +} pending_connection_t; + +/** Value of 'magic' field for cached_resolve_t. Used to try to catch bad + * pointers and memory stomping. */ +#define CACHED_RESOLVE_MAGIC 0x1234F00D + +/* Possible states for a cached resolve_t */ +/** We are waiting for the resolver system to tell us an answer here. + * When we get one, or when we time out, the state of this cached_resolve_t + * will become "DONE" and we'll possibly add a CACHED + * entry. This cached_resolve_t will be in the hash table so that we will + * know not to launch more requests for this addr, but rather to add more + * connections to the pending list for the addr. */ +#define CACHE_STATE_PENDING 0 +/** This used to be a pending cached_resolve_t, and we got an answer for it. + * Now we're waiting for this cached_resolve_t to expire. This should + * have no pending connections, and should not appear in the hash table. */ +#define CACHE_STATE_DONE 1 +/** We are caching an answer for this address. This should have no pending + * connections, and should appear in the hash table. */ +#define CACHE_STATE_CACHED 2 + +/** @name status values for a single DNS request. + * + * @{ */ +/** The DNS request is in progress. */ +#define RES_STATUS_INFLIGHT 1 +/** The DNS request finished and gave an answer */ +#define RES_STATUS_DONE_OK 2 +/** The DNS request finished and gave an error */ +#define RES_STATUS_DONE_ERR 3 +/**@}*/ + +/** A DNS request: possibly completed, possibly pending; cached_resolve + * structs are stored at the OR side in a hash table, and as a linked + * list from oldest to newest. + */ +typedef struct cached_resolve_t { + HT_ENTRY(cached_resolve_t) node; + uint32_t magic; /**< Must be CACHED_RESOLVE_MAGIC */ + char address[MAX_ADDRESSLEN]; /**< The hostname to be resolved. */ + + union { + uint32_t addr_ipv4; /**< IPv4 addr for <b>address</b>, if successful. + * (In host order.) */ + int err_ipv4; /**< One of DNS_ERR_*, if IPv4 lookup failed. */ + } result_ipv4; /**< Outcome of IPv4 lookup */ + union { + struct in6_addr addr_ipv6; /**< IPv6 addr for <b>address</b>, if + * successful */ + int err_ipv6; /**< One of DNS_ERR_*, if IPv6 lookup failed. */ + } result_ipv6; /**< Outcome of IPv6 lookup, if any */ + union { + char *hostname; /** A hostname, if PTR lookup happened successfully*/ + int err_hostname; /** One of DNS_ERR_*, if PTR lookup failed. */ + } result_ptr; + /** @name Status fields + * + * These take one of the RES_STATUS_* values, depending on the state + * of the corresponding lookup. + * + * @{ */ + unsigned int res_status_ipv4 : 2; + unsigned int res_status_ipv6 : 2; + unsigned int res_status_hostname : 2; + /**@}*/ + uint8_t state; /**< Is this cached entry pending/done/informative? */ + + time_t expire; /**< Remove items from cache after this time. */ + uint32_t ttl_ipv4; /**< What TTL did the nameserver tell us? */ + uint32_t ttl_ipv6; /**< What TTL did the nameserver tell us? */ + uint32_t ttl_hostname; /**< What TTL did the nameserver tell us? */ + /** Connections that want to know when we get an answer for this resolve. */ + pending_connection_t *pending_connections; + /** Position of this element in the heap*/ + int minheap_idx; +} cached_resolve_t; + +#endif + diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index f7710908bd..74f17ce78c 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -1,8 +1,9 @@ -/* Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** - * \file dnsserv.c \brief Implements client-side DNS proxy server code. Note: + * \file dnsserv.c + * \brief Implements client-side DNS proxy server code. Note: * this is the DNS Server code, not the Server DNS code. Confused? This code * runs on client-side, and acts as a DNS server. The code in dns.c, on the * other hand, runs on Tor servers, and acts as a DNS client. @@ -87,8 +88,6 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) for (i = 0; i < req->nquestions; ++i) { if (req->questions[i]->dns_question_class != EVDNS_CLASS_INET) continue; - if (! q) - q = req->questions[i]; switch (req->questions[i]->type) { case EVDNS_TYPE_A: case EVDNS_TYPE_AAAA: @@ -96,7 +95,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) /* We always pick the first one of these questions, if there is one. */ if (! supported_q) - supported_q = q; + supported_q = req->questions[i]; break; default: break; @@ -125,6 +124,7 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); conn = ENTRY_TO_EDGE_CONN(entry_conn); + CONNECTION_AP_EXPECT_NONPENDING(entry_conn); TO_CONN(conn)->state = AP_CONN_STATE_RESOLVE_WAIT; conn->is_dns_request = 1; @@ -199,6 +199,7 @@ dnsserv_launch_request(const char *name, int reverse, /* Make a new dummy AP connection, and attach the request to it. */ entry_conn = entry_connection_new(CONN_TYPE_AP, AF_INET); conn = ENTRY_TO_EDGE_CONN(entry_conn); + CONNECTION_AP_EXPECT_NONPENDING(entry_conn); conn->base_.state = AP_CONN_STATE_RESOLVE_WAIT; tor_addr_copy(&TO_CONN(conn)->addr, &control_conn->base_.addr); diff --git a/src/or/dnsserv.h b/src/or/dnsserv.h index 09ad5d7759..ad0e248c83 100644 --- a/src/or/dnsserv.h +++ b/src/or/dnsserv.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 30108b6041..310a948b35 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -87,7 +87,7 @@ get_entry_guards(void) /** Check whether the entry guard <b>e</b> is usable, given the directory * authorities' opinion about the router (stored in <b>ri</b>) and the user's - * configuration (in <b>options</b>). Set <b>e</b>->bad_since + * configuration (in <b>options</b>). Set <b>e</b>->bad_since * accordingly. Return true iff the entry guard's status changes. * * If it's not usable, set *<b>reason</b> to a static string explaining why. @@ -117,6 +117,9 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node, *reason = "not recommended as a guard"; else if (routerset_contains_node(options->ExcludeNodes, node)) *reason = "excluded"; + /* We only care about OR connection connectivity for entry guards. */ + else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) + *reason = "unreachable by config"; else if (e->path_bias_disabled) *reason = "path-biased"; @@ -141,8 +144,7 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node, } if (node) { - int is_dir = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; + int is_dir = node_is_dir(node); if (options->UseBridges && node_is_a_configured_bridge(node)) is_dir = 1; if (e->is_dir_cache != is_dir) { @@ -269,7 +271,7 @@ entry_is_live(const entry_guard_t *e, entry_is_live_flags_t flags, *msg = "not fast/stable"; return NULL; } - if (!fascist_firewall_allows_node(node)) { + if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)) { *msg = "unreachable by config"; return NULL; } @@ -398,10 +400,10 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, entry->bad_since = 0; entry->can_retry = 1; } - entry->is_dir_cache = node->rs && - node->rs->version_supports_microdesc_cache; + entry->is_dir_cache = node_is_dir(node); if (get_options()->UseBridges && node_is_a_configured_bridge(node)) entry->is_dir_cache = 1; + return NULL; } } else if (!for_directory) { @@ -432,8 +434,7 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, node_describe(node)); strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname)); memcpy(entry->identity, node->identity, DIGEST_LEN); - entry->is_dir_cache = node_is_dir(node) && node->rs && - node->rs->version_supports_microdesc_cache; + entry->is_dir_cache = node_is_dir(node); if (get_options()->UseBridges && node_is_a_configured_bridge(node)) entry->is_dir_cache = 1; @@ -442,7 +443,8 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, * don't all select them on the same day, and b) avoid leaving a * precise timestamp in the state file about when we first picked * this guard. For details, see the Jan 2010 or-dev thread. */ - entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); + time_t now = time(NULL); + entry->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now); entry->chosen_by_version = tor_strdup(VERSION); /* Are we picking this guard because all of our current guards are @@ -571,22 +573,6 @@ remove_obsolete_entry_guards(time_t now) } else if (tor_version_parse(ver, &v)) { msg = "does not seem to be from any recognized version of Tor"; version_is_bad = 1; - } else { - char *tor_ver = NULL; - tor_asprintf(&tor_ver, "Tor %s", ver); - if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") && - !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) || - (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.0.6-alpha")) || - /* above are bug 440; below are bug 1217 */ - (tor_version_as_new_as(tor_ver, "0.2.1.3-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.1.23")) || - (tor_version_as_new_as(tor_ver, "0.2.2.0-alpha") && - !tor_version_as_new_as(tor_ver, "0.2.2.7-alpha"))) { - msg = "was selected without regard for guard bandwidth"; - version_is_bad = 1; - } - tor_free(tor_ver); } if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) { /* It's been too long since the date listed in our state file. */ @@ -935,7 +921,8 @@ entry_guards_set_from_config(const or_options_t *options) } else if (routerset_contains_node(options->ExcludeNodes, node)) { SMARTLIST_DEL_CURRENT(entry_nodes, node); continue; - } else if (!fascist_firewall_allows_node(node)) { + } else if (!fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, + 0)) { SMARTLIST_DEL_CURRENT(entry_nodes, node); continue; } else if (! node->is_possible_guard) { @@ -989,39 +976,6 @@ entry_list_is_constrained(const or_options_t *options) return 0; } -/** Return true iff this node can answer directory questions about - * microdescriptors. */ -static int -node_understands_microdescriptors(const node_t *node) -{ - tor_assert(node); - if (node->rs && node->rs->version_supports_microdesc_cache) - return 1; - if (node->ri && tor_version_supports_microdescriptors(node->ri->platform)) - return 1; - return 0; -} - -/** Return true iff <b>node</b> is able to answer directory questions - * of type <b>dirinfo</b>. Always returns true if <b>dirinfo</b> is - * NO_DIRINFO (zero). */ -static int -node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo) -{ - /* Checking dirinfo for any type other than microdescriptors isn't required - yet, since we only choose directory guards that can support microdescs, - routerinfos, and networkstatuses, AND we don't use directory guards if - we're configured to do direct downloads of anything else. The only case - where we might have a guard that doesn't know about a type of directory - information is when we're retrieving directory information from a - bridge. */ - - if ((dirinfo & MICRODESC_DIRINFO) && - !node_understands_microdescriptors(node)) - return 0; - return 1; -} - /** Pick a live (up and listed) entry guard from entry_guards. If * <b>state</b> is non-NULL, this is for a specific circuit -- * make sure not to pick this circuit's exit or any node in the @@ -1077,6 +1031,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, int retval = 0; entry_is_live_flags_t entry_flags = 0; + (void) dirinfo_type; + { /* Set the flags we want our entry node to have */ if (need_uptime) { entry_flags |= ENTRY_NEED_UPTIME; @@ -1108,9 +1064,6 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, continue; /* don't pick the same node for entry and exit */ if (smartlist_contains(exit_family, node)) continue; /* avoid relays that are family members of our exit */ - if (dirinfo_type != NO_DIRINFO && - !node_can_handle_dirinfo(node, dirinfo_type)) - continue; /* this node won't be able to answer our dir questions */ smartlist_add(live_entry_guards, (void*)node); if (!entry->made_contact) { /* Always start with the first not-yet-contacted entry @@ -1203,7 +1156,7 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory, } else { /* Try to have at least 2 choices available. This way we don't * get stuck with a single live-but-crummy entry and just keep - * using him. + * using it. * (We might get 2 live-but-crummy entry guards, but so be it.) */ preferred_min = 2; } @@ -1491,8 +1444,9 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) } } else { if (state_version) { + time_t now = time(NULL); + e->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now); e->chosen_by_version = tor_strdup(state_version); - e->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30); } } if (e->path_bias_disabled && !e->bad_since) @@ -1841,7 +1795,7 @@ get_configured_bridge_by_orports_digest(const char *digest, } /** If we have a bridge configured whose digest matches <b>digest</b>, or a - * bridge with no known digest whose address matches <b>addr</b>:<b>/port</b>, + * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, * return that bridge. Else return NULL. If <b>digest</b> is NULL, check for * address/port matches only. */ static bridge_info_t * @@ -1864,6 +1818,30 @@ get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, return NULL; } +/** If we have a bridge configured whose digest matches <b>digest</b>, or a + * bridge with no known digest whose address matches <b>addr</b>:<b>port</b>, + * return 1. Else return 0. If <b>digest</b> is NULL, check for + * address/port matches only. */ +int +addr_is_a_configured_bridge(const tor_addr_t *addr, + uint16_t port, + const char *digest) +{ + tor_assert(addr); + return get_configured_bridge_by_addr_port_digest(addr, port, digest) ? 1 : 0; +} + +/** If we have a bridge configured whose digest matches + * <b>ei->identity_digest</b>, or a bridge with no known digest whose address + * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0. + * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */ +int +extend_info_is_a_configured_bridge(const extend_info_t *ei) +{ + const char *digest = ei->onion_key ? ei->identity_digest : NULL; + return addr_is_a_configured_bridge(&ei->addr, ei->port, digest); +} + /** Wrapper around get_configured_bridge_by_addr_port_digest() to look * it up via router descriptor <b>ri</b>. */ static bridge_info_t * @@ -2166,8 +2144,18 @@ launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge) return; } - directory_initiate_command(&bridge->addr, - bridge->port, 0/*no dirport*/, + /* Until we get a descriptor for the bridge, we only know one address for + * it. */ + if (!fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + FIREWALL_OR_CONNECTION, 0, 0)) { + log_notice(LD_CONFIG, "Tried to fetch a descriptor directly from a " + "bridge, but that bridge is not reachable through our " + "firewall."); + return; + } + + directory_initiate_command(&bridge->addr, bridge->port, + NULL, 0, /*no dirport*/ bridge->identity, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_BRIDGE, @@ -2228,7 +2216,9 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now) !options->UpdateBridgesFromAuthority, !num_bridge_auths); if (ask_bridge_directly && - !fascist_firewall_allows_address_or(&bridge->addr, bridge->port)) { + !fascist_firewall_allows_address_addr(&bridge->addr, bridge->port, + FIREWALL_OR_CONNECTION, 0, + 0)) { log_notice(LD_DIR, "Bridge at '%s' isn't reachable by our " "firewall policy. %s.", fmt_addrport(&bridge->addr, bridge->port), @@ -2255,7 +2245,7 @@ fetch_bridge_descriptors(const or_options_t *options, time_t now) log_info(LD_DIR, "Fetching bridge info '%s' from bridge authority.", resource); directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_BRIDGE, resource, 0); + ROUTER_PURPOSE_BRIDGE, resource, 0, DL_WANT_AUTHORITY); } } SMARTLIST_FOREACH_END(bridge); @@ -2276,6 +2266,7 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) * does so through an address from any source other than node_get_addr(). */ tor_addr_t addr; + const or_options_t *options = get_options(); if (node->ri) { routerinfo_t *ri = node->ri; @@ -2308,9 +2299,15 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } } - /* Mark which address to use based on which bridge_t we got. */ - node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && - !tor_addr_is_null(&node->ri->ipv6_addr)); + if (options->ClientPreferIPv6ORPort == -1) { + /* Mark which address to use based on which bridge_t we got. */ + node->ipv6_preferred = (tor_addr_family(&bridge->addr) == AF_INET6 && + !tor_addr_is_null(&node->ri->ipv6_addr)); + } else { + /* Mark which address to use based on user preference */ + node->ipv6_preferred = (fascist_firewall_prefer_ipv6_orport(options) && + !tor_addr_is_null(&node->ri->ipv6_addr)); + } /* XXXipv6 we lack support for falling back to another address for the same relay, warn the user */ @@ -2319,10 +2316,13 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) node_get_pref_orport(node, &ap); log_notice(LD_CONFIG, "Bridge '%s' has both an IPv4 and an IPv6 address. " - "Will prefer using its %s address (%s).", + "Will prefer using its %s address (%s) based on %s.", ri->nickname, - tor_addr_family(&ap.addr) == AF_INET6 ? "IPv6" : "IPv4", - fmt_addrport(&ap.addr, ap.port)); + node->ipv6_preferred ? "IPv6" : "IPv4", + fmt_addrport(&ap.addr, ap.port), + options->ClientPreferIPv6ORPort == -1 ? + "the configured Bridge address" : + "ClientPreferIPv6ORPort"); } } if (node->rs) { @@ -2484,11 +2484,9 @@ any_bridge_supports_microdescriptors(void) SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { node = node_get_by_id(e->identity); if (node && node->is_running && - node_is_bridge(node) && node_is_a_configured_bridge(node) && - node_understands_microdescriptors(node)) { + node_is_bridge(node) && node_is_a_configured_bridge(node)) { /* This is one of our current bridges, and we know enough about - * it to know that it will be able to answer our microdescriptor - * questions. */ + * it to know that it will be able to answer our questions. */ return 1; } } SMARTLIST_FOREACH_END(e); diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 107a562506..247c80940e 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -127,6 +127,9 @@ int getinfo_helper_entry_guards(control_connection_t *conn, void mark_bridge_list(void); void sweep_bridge_list(void); +int addr_is_a_configured_bridge(const tor_addr_t *addr, uint16_t port, + const char *digest); +int extend_info_is_a_configured_bridge(const extend_info_t *ei); int routerinfo_is_a_configured_bridge(const routerinfo_t *ri); int node_is_a_configured_bridge(const node_t *node); void learned_router_identity(const tor_addr_t *addr, uint16_t port, diff --git a/src/or/eventdns_tor.h b/src/or/eventdns_tor.h index 9d51f0960e..5db09ae043 100644 --- a/src/or/eventdns_tor.h +++ b/src/or/eventdns_tor.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_EVENTDNS_TOR_H @@ -12,9 +12,6 @@ typedef unsigned int uint; #ifndef HAVE_U_CHAR typedef unsigned char u_char; #endif -#ifdef _WIN32 -#define inline __inline -#endif #include "torint.h" /* These are for debugging possible memory leaks. */ diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index e8c8aa60a4..aa1b3e26fe 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -151,7 +151,7 @@ init_ext_or_cookie_authentication(int is_enabled) } /** Read data from <b>conn</b> and see if the client sent us the - * authentication type that she prefers to use in this session. + * authentication type that they prefer to use in this session. * * Return -1 if we received corrupted data or if we don't support the * authentication type. Return 0 if we need more data in @@ -178,7 +178,7 @@ connection_ext_or_auth_neg_auth_type(connection_t *conn) return 1; } -/** DOCDOC */ +/* DOCDOC */ STATIC int handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len, char **client_hash_out, @@ -193,8 +193,7 @@ handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len, return -1; /* Get our nonce */ - if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0) - return -1; + crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN); { /* set up macs */ size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) + diff --git a/src/or/ext_orport.h b/src/or/ext_orport.h index 8b2542f937..33d954e8d0 100644 --- a/src/or/ext_orport.h +++ b/src/or/ext_orport.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef EXT_ORPORT_H diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc new file mode 100644 index 0000000000..f6289199b7 --- /dev/null +++ b/src/or/fallback_dirs.inc @@ -0,0 +1,281 @@ +/* To comment-out entries in this file, use C comments, and add * to the start of each line. (stem finds fallback entries using " at the start of a line.) */ +/* Whitelist & blacklist excluded 1273 of 1553 candidates. */ +/* Checked IPv4 DirPorts served a consensus within 15.0s. */ +/* +Final Count: 100 (Eligible 280, Target 356 (1781 * 0.20), Max 100) +Excluded: 180 (Same Operator 102, Failed/Skipped Download 40, Excess 38) +Bandwidth Range: 6.0 - 67.2 MB/s +*/ +/* +Onionoo Source: details Date: 2016-04-18 01:00:00 Version: 3.1 +URL: https:onionoo.torproject.orgdetails?fields=fingerprint%2Cnickname%2Ccontact%2Clast_changed_address_or_port%2Cconsensus_weight%2Cadvertised_bandwidth%2Cor_addresses%2Cdir_address%2Crecommended_version%2Cflags%2Ceffective_family&flag=V2Dir&type=relay&last_seen_days=-7&first_seen_days=7- +*/ +/* +Onionoo Source: uptime Date: 2016-04-18 01:00:00 Version: 3.1 +URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay&last_seen_days=-7 +*/ +"193.171.202.146:9030 orport=9001 id=01A9258A46E97FF8B2CAC7910577862C14F2C524" +" weight=10", +"5.9.110.236:9030 orport=9001 id=0756B7CD4DFC8182BE23143FAC0642F515182CEB" +" ipv6=[2a01:4f8:162:51e2::2]:9001" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but opted-out before 0.2.8.6 + * "37.187.1.149:9030 orport=9001 id=08DC0F3C6E3D9C527C1FC8745D35DD1B0DE1875D" + * " ipv6=[2001:41d0:a:195::1]:9001" + * " weight=10", + */ +"5.39.92.199:80 orport=443 id=0BEA4A88D069753218EAAAD6D22EA87B9A1319D6" +" weight=10", +"5.196.88.122:9030 orport=9001 id=0C2C599AFCB26F5CFC2C7592435924C1D63D9484" +" weight=10", +"178.62.197.82:80 orport=443 id=0D3EBA17E1C78F1E9900BABDB23861D46FCAF163" +" weight=10", +/* Fallback was on 0.2.8.6 list, but down for a week before 0.2.9 + * "144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB" + * " ipv6=[2a01:4f8:190:9490::dead]:443" + * " weight=10", + */ +"178.32.216.146:9030 orport=9001 id=17898F9A2EBC7D69DAF87C00A1BD2FABF3C9E1D2" +" weight=10", +"46.101.151.222:80 orport=443 id=1DBAED235E3957DE1ABD25B4206BE71406FB61F8" +" weight=10", +"91.219.237.229:80 orport=443 id=1ECD73B936CB6E6B3CD647CC204F108D9DF2C9F7" +" weight=10", +"212.47.229.2:9030 orport=9001 id=20462CBA5DA4C2D963567D17D0B7249718114A68" +" weight=10", +"185.61.138.18:8080 orport=4443 id=2541759BEC04D37811C2209A88E863320271EC9C" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 + * "51.254.215.121:80 orport=443 id=262B66AD25C79588AD1FC8ED0E966395B47E5C1D" + * " weight=10", + */ +/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 + * "194.150.168.79:11112 orport=11111 id=29F1020B94BE25E6BE1AD13E93CE19D2131B487C" + * " weight=10", + */ +"144.76.26.175:9012 orport=9011 id=2BA2C8E96B2590E1072AECE2BDB5C48921BF8510" +" weight=10", +"62.210.124.124:9130 orport=9101 id=2EBD117806EE43C3CC885A8F1E4DC60F207E7D3E" +" ipv6=[2001:bc8:3f23:100::1]:9101" +" weight=10", +"213.61.66.118:9031 orport=9001 id=30648BC64CEDB3020F4A405E4AB2A6347FB8FA22" +" weight=10", +"212.83.154.33:8080 orport=8443 id=322C6E3A973BC10FC36DE3037AD27BC89F14723B" +" weight=10", +"109.105.109.162:52860 orport=60784 id=32EE911D968BE3E016ECA572BB1ED0A9EE43FC2F" +" ipv6=[2001:948:7:2::163]:5001" +" weight=10", +"146.0.32.144:9030 orport=9001 id=35E8B344F661F4F2E68B17648F35798B44672D7E" +" weight=10", +"217.79.190.25:9030 orport=9090 id=361D33C96D0F161275EE67E2C91EE10B276E778B" +" weight=10", +"62.210.92.11:9130 orport=9101 id=387B065A38E4DAA16D9D41C2964ECBC4B31D30FF" +" ipv6=[2001:bc8:338c::1]:9101" +" weight=10", +"198.50.191.95:80 orport=443 id=39F096961ED2576975C866D450373A9913AFDC92" +" weight=10", +"164.132.77.175:9030 orport=9001 id=3B33F6FCA645AD4E91428A3AF7DC736AD9FB727B" +" weight=10", +"176.10.107.180:9030 orport=9001 id=3D7E274A87D9A89AF064C13D1EE4CA1F184F2600" +" weight=10", +"37.187.102.186:9030 orport=9001 id=489D94333DF66D57FFE34D9D59CC2D97E2CB0053" +" ipv6=[2001:41d0:a:26ba::1]:9001" +" weight=10", +"188.165.194.195:9030 orport=9001 id=49E7AD01BB96F6FE3AB8C3B15BD2470B150354DF" +" weight=10", +"108.53.208.157:80 orport=443 id=4F0DB7E687FC7C0AE55C8F243DA8B0EB27FBF1F2" +" weight=10", +"212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2" +" ipv6=[2a02:168:6e00:0:3a60:77ff:fe9c:8bd1]:9001" +" weight=10", +/* Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 + * "5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33" + * " weight=10", + */ +"94.23.204.175:9030 orport=9001 id=5665A3904C89E22E971305EE8C1997BCA4123C69" +" weight=10", +"109.163.234.9:80 orport=443 id=5714542DCBEE1DD9864824723638FD44B2122CEA" +" weight=10", +"185.21.100.50:9030 orport=9001 id=58ED9C9C35E433EE58764D62892B4FFD518A3CD0" +" ipv6=[2a00:1158:2:cd00:0:74:6f:72]:443" +" weight=10", +"78.142.142.246:80 orport=443 id=5A5E03355C1908EBF424CAF1F3ED70782C0D2F74" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 + * "185.100.85.138:80 orport=46356 id=5C4DF16A0029CC4F67D3E127356E68F219269859" + * " weight=10", + */ +"178.16.208.62:80 orport=443 id=5CF8AFA5E4B0BB88942A44A3F3AAE08C3BDFD60B" +" ipv6=[2a00:1c20:4089:1234:a6a4:2926:d0af:dfee]:443" +" weight=10", +"95.128.43.164:80 orport=443 id=616081EC829593AF4232550DE6FFAA1D75B37A90" +" ipv6=[2a02:ec0:209:10::4]:443" +" weight=10", +"89.187.142.208:80 orport=443 id=64186650FFE4469EBBE52B644AE543864D32F43C" +" weight=10", +/* Fallback was on 0.2.8.6 list, but operator opted-out before 0.2.9 + * "144.76.73.140:9030 orport=9001 id=6A640018EABF3DA9BAD9321AA37C2C87BBE1F907" + * " weight=10", + */ +/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 + * "94.126.23.174:9030 orport=9001 id=6FC6F08270D565BE89B7C819DD8E2D487397C073" + * " weight=10", + */ +"176.31.191.26:9030 orport=9001 id=7350AB9ED7568F22745198359373C04AC783C37C" +" weight=10", +"46.101.237.246:9030 orport=9001 id=75F1992FD3F403E9C082A5815EB5D12934CDF46C" +" ipv6=[2a03:b0c0:3:d0::208:5001]:9050" +" weight=10", +"185.11.180.67:80 orport=9001 id=794D8EA8343A4E820320265D05D4FA83AB6D1778" +" weight=10", +"62.210.129.246:80 orport=443 id=79E169B25E4C7CE99584F6ED06F379478F23E2B8" +" weight=10", +"82.223.21.74:9030 orport=9001 id=7A32C9519D80CA458FC8B034A28F5F6815649A98" +" ipv6=[2001:470:53e0::cafe]:9050" +" weight=10", +"192.160.102.164:80 orport=9001 id=823AA81E277F366505545522CEDC2F529CE4DC3F" +" weight=10", +"192.87.28.82:9030 orport=9001 id=844AE9CAD04325E955E2BE1521563B79FE7094B7" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but changed address before 0.2.8.5 + * "84.219.173.60:9030 orport=443 id=855BC2DABE24C861CD887DB9B2E950424B49FC34" + * " weight=10", + */ +"163.172.138.22:80 orport=443 id=8664DC892540F3C789DB37008236C096C871734D" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but downloads were slow before 0.2.8.5 + * "185.96.88.29:80 orport=443 id=86C281AD135058238D7A337D546C902BE8505DDE" + * " weight=10", + */ +"93.180.156.84:9030 orport=9001 id=8844D87E9B038BE3270938F05AF797E1D3C74C0F" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but DirPort changed before 0.2.8.5 + * "178.217.184.32:9030 orport=443 id=8B7F47AE1A5D954A3E58ACDE0865D09DBA5B738D" + * " weight=10", + */ +"151.80.42.103:9030 orport=9001 id=9007C1D8E4F03D506A4A011B907A9E8D04E3C605" +" ipv6=[2001:41d0:e:f67::114]:9001" +" weight=10", +"5.79.68.161:81 orport=443 id=9030DCF419F6E2FBF84F63CBACBA0097B06F557E" +" ipv6=[2001:1af8:4700:a012:1::1]:443" +" weight=10", +"51.255.41.65:9030 orport=9001 id=9231DF741915AA1630031A93026D88726877E93A" +" weight=10", +"91.219.237.244:80 orport=443 id=92ECC9E0E2AF81BB954719B189AC362E254AD4A5" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but changed fingerprint before 0.2.8.5 + * "46.101.102.71:80 orport=443 id=9504CB22EEB25D344DE63CB7A6F2C46F895C3686" + * " ipv6=[2a03:b0c0:3:d0::2ed:7001]:9050" + * " weight=10", + */ +"85.214.206.219:9030 orport=9001 id=98F8D5F359949E41DE8DF3DBB1975A86E96A84A0" +" weight=10", +"81.7.10.93:31336 orport=31337 id=99E246DB480B313A3012BC3363093CC26CD209C7" +" weight=10", +"46.28.110.244:80 orport=443 id=9F7D6E6420183C2B76D3CE99624EBC98A21A967E" +" weight=10", +"46.165.230.5:80 orport=443 id=A0F06C2FADF88D3A39AA3072B406F09D7095AC9E" +" weight=10", +"171.25.193.77:80 orport=443 id=A10C4F666D27364036B562823E5830BC448E046A" +" ipv6=[2001:67c:289c:3::77]:443" +" weight=10", +"176.9.5.116:9030 orport=9001 id=A1EB8D8F1EE28DB98BBB1EAA3B4BEDD303BAB911" +" weight=10", +"192.34.63.137:9030 orport=443 id=ABCB4965F1FEE193602B50A365425105C889D3F8" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but had low uptime before 0.2.8.5 + * "195.154.164.243:80 orport=443 id=AC66FFA4AB35A59EBBF5BF4C70008BF24D8A7A5C" + * " ipv6=[2001:bc8:399f:f000::1]:993" + * " weight=10", + */ +"86.59.119.88:80 orport=443 id=ACD889D86E02EDDAB1AFD81F598C0936238DC6D0" +" weight=10", +"163.172.131.88:80 orport=443 id=AD253B49E303C6AB1E048B014392AC569E8A7DAE" +" ipv6=[2001:bc8:4400:2100::2:1009]:443" +" weight=10", +"178.254.44.135:80 orport=443 id=AE6A8C18E7499B586CD36246AC4BCAFFBBF93AB2" +" weight=10", +"37.187.7.74:80 orport=443 id=AEA43CB1E47BE5F8051711B2BF01683DB1568E05" +" ipv6=[2001:41d0:a:74a::1]:443" +" weight=10", +"212.129.62.232:80 orport=443 id=B143D439B72D239A419F8DCE07B8A8EB1B486FA7" +" weight=10", +"185.66.250.141:9030 orport=9001 id=B1726B94885CE3AC3910CA8B60622B97B98E2529" +" weight=10", +"193.11.114.46:9032 orport=9003 id=B83DC1558F0D34353BB992EF93AFEAFDB226A73E" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but downloads were slow before 0.2.8.5 + * "178.62.36.64:9030 orport=9001 id=B87C84E38DAECFFFFDE98E5AEE5786AFDC748F2C" + * " weight=10", + */ +"197.231.221.211:9030 orport=9001 id=BC630CBBB518BE7E9F4E09712AB0269E9DC7D626" +" weight=10", +"198.96.155.3:8080 orport=5001 id=BCEDF6C193AA687AE471B8A22EBF6BC57C2D285E" +" weight=10", +"148.251.190.229:9030 orport=9010 id=BF0FB582E37F738CD33C3651125F2772705BB8E8" +" ipv6=[2a01:4f8:211:c68::2]:9010" +" weight=10", +"188.138.112.60:1433 orport=1521 id=C414F28FD2BEC1553024299B31D4E726BEB8E788" +" weight=10", +"195.154.79.128:80 orport=443 id=C697612CA5AED06B8D829FCC6065B9287212CB2F" +" weight=10", +"37.59.46.159:9030 orport=9001 id=CBD0D1BD110EC52963082D839AC6A89D0AE243E7" +" weight=10", +"91.121.54.8:9030 orport=9001 id=CBEE0F3303C8C50462A12107CA2AE061831931BC" +" weight=10", +"178.62.199.226:80 orport=443 id=CBEFF7BA4A4062045133C053F2D70524D8BBE5BE" +" ipv6=[2a03:b0c0:2:d0::b7:5001]:443" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but went down before 0.2.8.5 + * "81.7.17.171:80 orport=443 id=CFECDDCA990E3EF7B7EC958B22441386B6B8D820" + * " ipv6=[2a02:180:1:1::517:11ab]:443" + * " weight=10", + */ +"134.119.3.164:9030 orport=9001 id=D1B8AAA98C65F3DF7D8BB3AF881CAEB84A33D8EE" +" weight=10", +"185.13.38.75:9030 orport=9001 id=D2A1703758A0FBBA026988B92C2F88BAB59F9361" +" weight=10", +"37.187.115.157:9030 orport=9001 id=D5039E1EBFD96D9A3F9846BF99EC9F75EDDE902A" +" weight=10", +/* Fallback was on 0.2.8.2-alpha list, but lost Guard flag before 0.2.8.5 + * "185.14.185.240:9030 orport=443 id=D62FB817B0288085FAC38A6DC8B36DCD85B70260" + * " weight=10", + */ +"37.221.162.226:9030 orport=9001 id=D64366987CB39F61AD21DBCF8142FA0577B92811" +" weight=10", +"193.35.52.53:9030 orport=9001 id=DAA39FC00B196B353C2A271459C305C429AF09E4" +" weight=10", +"178.62.173.203:9030 orport=9001 id=DD85503F2D1F52EF9EAD621E942298F46CD2FC10" +" ipv6=[2a03:b0c0:0:1010::a4:b001]:9001" +" weight=10", +"5.34.183.205:80 orport=443 id=DDD7871C1B7FA32CB55061E08869A236E61BDDF8" +" weight=10", +/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 + * "195.191.233.221:80 orport=443 id=DE134FC8E5CC4EC8A5DE66934E70AC9D70267197" + * " weight=10", + */ +"46.252.26.2:45212 orport=49991 id=E589316576A399C511A9781A73DA4545640B479D" +" weight=10", +/* Fallback was on 0.2.8.6 list, but went down before 0.2.9 + * "176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329" + * " ipv6=[2001:41d0:8:eb9d::1]:22" + * " weight=10", + */ +"131.188.40.188:443 orport=80 id=EBE718E1A49EE229071702964F8DB1F318075FF8" +" weight=10", +"91.219.236.222:80 orport=443 id=EC413181CEB1C8EDC17608BBB177CD5FD8535E99" +" weight=10", +"94.242.246.23:443 orport=9001 id=F65E0196C94DFFF48AFBF2F5F9E3E19AAE583FD0" +" ipv6=[2a01:608:ffff:ff07::1:23]:9003" +" weight=10", +"46.101.143.173:80 orport=443 id=F960DF50F0FD4075AC9B505C1D4FFC8384C490FB" +" weight=10", +/* Fallback was on 0.2.8.6 list, but changed IPv4 before 0.2.9 + * "195.154.8.111:80 orport=443 id=FCB6695F8F2DC240E974510A4B3A0F2B12AB5B64" + * " weight=10", + */ +"192.187.124.98:9030 orport=9001 id=FD1871854BFC06D7B02F10742073069F0528B5CC" +" weight=10", +"193.11.164.243:9030 orport=9001 id=FFA72BD683BC2FCF988356E6BEC1E490F313FB07" +" ipv6=[2001:6b0:7:125::243]:9001" +" weight=10", diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c index 42bebcd847..53b311e580 100644 --- a/src/or/fp_pair.c +++ b/src/or/fp_pair.c @@ -1,6 +1,14 @@ -/* Copyright (c) 2013-2015, The Tor Project, Inc. */ +/* Copyright (c) 2013-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file fp_pair.c + * + * \brief Manages data structures for associating pairs of fingerprints. Used + * to handle combinations of identity/signing-key fingerprints for + * authorities. + **/ + #include "or.h" #include "fp_pair.h" @@ -21,7 +29,7 @@ struct fp_pair_map_s { */ /** Compare fp_pair_entry_t objects by key value. */ -static INLINE int +static inline int fp_pair_map_entries_eq(const fp_pair_map_entry_t *a, const fp_pair_map_entry_t *b) { @@ -29,7 +37,7 @@ fp_pair_map_entries_eq(const fp_pair_map_entry_t *a, } /** Return a hash value for an fp_pair_entry_t. */ -static INLINE unsigned int +static inline unsigned int fp_pair_map_entry_hash(const fp_pair_map_entry_t *a) { tor_assert(sizeof(a->key) == DIGEST_LEN*2); diff --git a/src/or/fp_pair.h b/src/or/fp_pair.h index 0830ab1f36..b1466581d2 100644 --- a/src/or/fp_pair.h +++ b/src/or/fp_pair.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Tor Project, Inc. */ +/* Copyright (c) 2013-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/geoip.c b/src/or/geoip.c index 120ce479cc..b563db0418 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2007-2015, The Tor Project, Inc. */ +/* Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -18,7 +18,6 @@ #include "geoip.h" #include "routerlist.h" -static void clear_geoip_db(void); static void init_geoip_countries(void); /** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */ @@ -483,7 +482,7 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history = HT_INITIALIZER(); /** Hashtable helper: compute a hash of a clientmap_entry_t. */ -static INLINE unsigned +static inline unsigned clientmap_entry_hash(const clientmap_entry_t *a) { unsigned h = (unsigned) tor_addr_hash(&a->addr); @@ -494,7 +493,7 @@ clientmap_entry_hash(const clientmap_entry_t *a) return h; } /** Hashtable helper: compare two clientmap_entry_t values for equality. */ -static INLINE int +static inline int clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b) { if (strcmp_opt(a->transport_name, b->transport_name)) @@ -970,7 +969,7 @@ geoip_get_dirreq_history(dirreq_type_t type) &ent->completion_time); if (time_diff == 0) time_diff = 1; /* Avoid DIV/0; "instant" answers are impossible - * by law of nature or something, but a milisecond + * by law of nature or something, but a millisecond * is a bit greater than "instantly" */ bytes_per_second = (uint32_t)(1000 * ent->response_size / time_diff); dltimes[ent_sl_idx] = bytes_per_second; @@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now) { char t[ISO_TIME_LEN+1]; int i; - char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string, - *v3_tunneled_dl_string; - char *result; + char *v3_ips_string = NULL, *v3_reqs_string = NULL, + *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL; + char *result = NULL; if (!start_of_dirreq_stats_interval) return NULL; /* Not initialized. */ @@ -1280,6 +1279,8 @@ geoip_dirreq_stats_write(time_t now) /* Generate history string .*/ str = geoip_format_dirreq_stats(now); + if (! str) + goto done; /* Write dirreq-stats string to disk. */ if (!check_or_create_data_subdir("stats")) { @@ -1666,7 +1667,7 @@ getinfo_helper_geoip(control_connection_t *control_conn, } /** Release all storage held by the GeoIP databases and country list. */ -static void +STATIC void clear_geoip_db(void) { if (geoip_countries) { diff --git a/src/or/geoip.h b/src/or/geoip.h index 8a3486c7ac..070296dd07 100644 --- a/src/or/geoip.h +++ b/src/or/geoip.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -18,6 +18,7 @@ STATIC int geoip_parse_entry(const char *line, sa_family_t family); STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr); STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr); +STATIC void clear_geoip_db(void); #endif int should_record_bridge_info(const or_options_t *options); int geoip_load_file(sa_family_t family, const char *filename); diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 356e11f6ec..9408925d96 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -412,11 +412,15 @@ configure_accounting(time_t now) /** Return the relevant number of bytes sent/received this interval * based on the set AccountingRule */ -static uint64_t +uint64_t get_accounting_bytes(void) { if (get_options()->AccountingRule == ACCT_SUM) return n_bytes_read_in_interval+n_bytes_written_in_interval; + else if (get_options()->AccountingRule == ACCT_IN) + return n_bytes_read_in_interval; + else if (get_options()->AccountingRule == ACCT_OUT) + return n_bytes_written_in_interval; else return MAX(n_bytes_read_in_interval, n_bytes_written_in_interval); } @@ -490,7 +494,7 @@ reset_accounting(time_t now) } /** Return true iff we should save our bandwidth usage to disk. */ -static INLINE int +static inline int time_to_record_bandwidth_usage(time_t now) { /* Note every 600 sec */ @@ -1010,7 +1014,7 @@ getinfo_helper_accounting(control_connection_t *conn, else *answer = tor_strdup("awake"); } else if (!strcmp(question, "accounting/bytes")) { - tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, + tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, U64_PRINTF_ARG(n_bytes_read_in_interval), U64_PRINTF_ARG(n_bytes_written_in_interval)); } else if (!strcmp(question, "accounting/bytes-left")) { @@ -1022,6 +1026,18 @@ getinfo_helper_accounting(control_connection_t *conn, total_left = limit - total_bytes; tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, U64_PRINTF_ARG(total_left), U64_PRINTF_ARG(total_left)); + } else if (get_options()->AccountingRule == ACCT_IN) { + uint64_t read_left = 0; + if (n_bytes_read_in_interval < limit) + read_left = limit - n_bytes_read_in_interval; + tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, + U64_PRINTF_ARG(read_left), U64_PRINTF_ARG(limit)); + } else if (get_options()->AccountingRule == ACCT_OUT) { + uint64_t write_left = 0; + if (n_bytes_written_in_interval < limit) + write_left = limit - n_bytes_written_in_interval; + tor_asprintf(answer, U64_FORMAT" "U64_FORMAT, + U64_PRINTF_ARG(limit), U64_PRINTF_ARG(write_left)); } else { uint64_t read_left = 0, write_left = 0; if (n_bytes_read_in_interval < limit) diff --git a/src/or/hibernate.h b/src/or/hibernate.h index b9e619c5ad..fa9da6de39 100644 --- a/src/or/hibernate.h +++ b/src/or/hibernate.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -19,6 +19,7 @@ MOCK_DECL(int, accounting_is_enabled, (const or_options_t *options)); int accounting_get_interval_length(void); MOCK_DECL(time_t, accounting_get_end_time, (void)); void configure_accounting(time_t now); +uint64_t get_accounting_bytes(void); void accounting_run_housekeeping(time_t now); void accounting_add_bytes(size_t n_read, size_t n_written, int seconds); int accounting_record_bandwidth_usage(time_t now, or_state_t *state); diff --git a/src/or/include.am b/src/or/include.am index b44e1099dc..712ae18406 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -15,7 +15,7 @@ else tor_platform_source= endif -EXTRA_DIST+= src/or/ntmain.c src/or/or_sha1.i src/or/Makefile.nmake +EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake if USE_EXTERNAL_EVDNS evdns_source= @@ -43,16 +43,18 @@ LIBTOR_A_SOURCES = \ src/or/connection_or.c \ src/or/control.c \ src/or/cpuworker.c \ + src/or/dircollate.c \ src/or/directory.c \ src/or/dirserv.c \ src/or/dirvote.c \ src/or/dns.c \ src/or/dnsserv.c \ - src/or/fp_pair.c \ + src/or/fp_pair.c \ src/or/geoip.c \ src/or/entrynodes.c \ src/or/ext_orport.c \ src/or/hibernate.c \ + src/or/keypin.c \ src/or/main.c \ src/or/microdesc.c \ src/or/networkstatus.c \ @@ -61,9 +63,11 @@ LIBTOR_A_SOURCES = \ src/or/onion_fast.c \ src/or/onion_tap.c \ src/or/transports.c \ + src/or/periodic.c \ src/or/policies.c \ src/or/reasons.c \ src/or/relay.c \ + src/or/rendcache.c \ src/or/rendclient.c \ src/or/rendcommon.c \ src/or/rendmid.c \ @@ -71,34 +75,32 @@ LIBTOR_A_SOURCES = \ src/or/rephist.c \ src/or/replaycache.c \ src/or/router.c \ + src/or/routerkeys.c \ src/or/routerlist.c \ src/or/routerparse.c \ src/or/routerset.c \ src/or/scheduler.c \ src/or/statefile.c \ src/or/status.c \ + src/or/torcert.c \ src/or/onion_ntor.c \ $(evdns_source) \ - $(tor_platform_source) \ - src/or/config_codedigest.c + $(tor_platform_source) src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES) src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES) -#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \ -# ../common/libor-event.a - - src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or -src/or/tor_main.o: micro-revision.i +src/or/tor_main.$(OBJEXT) \ + src/or/src_or_tor_cov-tor_main.$(OBJEXT): micro-revision.i -AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ - -DLOCALSTATEDIR="\"$(localstatedir)\"" \ - -DBINDIR="\"$(bindir)\"" +AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \ + -DLOCALSTATEDIR="\"$(localstatedir)\"" \ + -DBINDIR="\"$(bindir)\"" -src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_libtor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) # -L flags need to go in LDFLAGS. -l flags need to go in LDADD. @@ -108,24 +110,21 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \ - src/common/libor-crypto.a $(LIBDONNA) \ - src/common/libor-event.a \ + src/common/libor-crypto.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + src/common/libor-event.a src/trunnel/libor-trunnel.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ if COVERAGE_ENABLED src_or_tor_cov_SOURCES = src/or/tor_main.c -src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS) +src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \ - src/common/libor-crypto-testing.a $(LIBDONNA) \ - src/common/libor-event-testing.a \ + src/common/libor-crypto-testing.a $(LIBKECCAK_TINY) $(LIBDONNA) \ + src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ -TESTING_TOR_BINARY = ./src/or/tor-cov -else -TESTING_TOR_BINARY = ./src/or/tor endif ORHEADERS = \ @@ -148,17 +147,21 @@ ORHEADERS = \ src/or/connection_or.h \ src/or/control.h \ src/or/cpuworker.h \ + src/or/dircollate.h \ src/or/directory.h \ src/or/dirserv.h \ src/or/dirvote.h \ src/or/dns.h \ + src/or/dns_structs.h \ src/or/dnsserv.h \ src/or/eventdns_tor.h \ src/or/ext_orport.h \ + src/or/fallback_dirs.inc \ src/or/fp_pair.h \ src/or/geoip.h \ src/or/entrynodes.h \ src/or/hibernate.h \ + src/or/keypin.h \ src/or/main.h \ src/or/microdesc.h \ src/or/networkstatus.h \ @@ -170,9 +173,11 @@ ORHEADERS = \ src/or/onion_tap.h \ src/or/or.h \ src/or/transports.h \ + src/or/periodic.h \ src/or/policies.h \ src/or/reasons.h \ src/or/relay.h \ + src/or/rendcache.h \ src/or/rendclient.h \ src/or/rendcommon.h \ src/or/rendmid.h \ @@ -180,45 +185,36 @@ ORHEADERS = \ src/or/rephist.h \ src/or/replaycache.h \ src/or/router.h \ + src/or/routerkeys.h \ src/or/routerlist.h \ + src/or/routerkeys.h \ src/or/routerset.h \ src/or/routerparse.h \ src/or/scheduler.h \ src/or/statefile.h \ - src/or/status.h + src/or/status.h \ + src/or/torcert.h noinst_HEADERS+= $(ORHEADERS) micro-revision.i -src/or/config_codedigest.o: src/or/or_sha1.i - micro-revision.i: FORCE - @rm -f micro-revision.tmp; \ - if test -d "$(top_srcdir)/.git" && \ - test -x "`which git 2>&1;true`"; then \ - HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ - echo \"$$HASH\" > micro-revision.tmp; \ - fi; \ - if test ! -f micro-revision.tmp ; then \ - if test ! -f micro-revision.i ; then \ - echo '""' > micro-revision.i; \ - fi; \ - elif test ! -f micro-revision.i || \ - test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ - mv micro-revision.tmp micro-revision.i; \ - fi; true - -src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) - $(AM_V_GEN)if test "@SHA1SUM@" != none; then \ - (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \ - "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \ - elif test "@OPENSSL@" != none; then \ - (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \ - "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \ - else \ - rm src/or/or_sha1.i; \ - touch src/or/or_sha1.i; \ - fi - -CLEANFILES+= micro-revision.i src/or/micro-revision.i + $(AM_V_at)rm -f micro-revision.tmp; \ + if test -d "$(top_srcdir)/.git" && \ + test -x "`which git 2>&1;true`"; then \ + HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ + echo \"$$HASH\" > micro-revision.tmp; \ + fi; \ + if test ! -f micro-revision.tmp; then \ + if test ! -f micro-revision.i; then \ + echo '""' > micro-revision.i; \ + fi; \ + elif test ! -f micro-revision.i || \ + test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \ + mv micro-revision.tmp micro-revision.i; \ + fi; \ + rm -f micro-revision.tmp; \ + true + +CLEANFILES+= micro-revision.i src/or/micro-revision.i micro-revision.tmp FORCE: diff --git a/src/or/keypin.c b/src/or/keypin.c new file mode 100644 index 0000000000..1f82eccf86 --- /dev/null +++ b/src/or/keypin.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file keypin.c + * + * \brief Functions and structures for associating routers' RSA key + * fingerprints with their ED25519 keys. + */ + +#define KEYPIN_PRIVATE + +#include "orconfig.h" +#include "compat.h" +#include "crypto.h" +#include "crypto_format.h" +#include "di_ops.h" +#include "ht.h" +#include "keypin.h" +#include "siphash.h" +#include "torint.h" +#include "torlog.h" +#include "util.h" +#include "util_format.h" + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#ifdef _WIN32 +#include <io.h> +#endif + +/** + * @file keypin.c + * @brief Key-pinning for RSA and Ed25519 identity keys at directory + * authorities. + * + * This module implements a key-pinning mechanism to ensure that it's safe + * to use RSA keys as identitifers even as we migrate to Ed25519 keys. It + * remembers, for every Ed25519 key we've seen, what the associated Ed25519 + * key is. This way, if we see a different Ed25519 key with that RSA key, + * we'll know that there's a mismatch. + * + * We persist these entries to disk using a simple format, where each line + * has a base64-encoded RSA SHA1 hash, then a base64-endoded Ed25519 key. + * Empty lines, misformed lines, and lines beginning with # are + * ignored. Lines beginning with @ are reserved for future extensions. + */ + +static int keypin_journal_append_entry(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key); +static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + const int do_not_add, + const int replace); +static int keypin_add_or_replace_entry_in_map(keypin_ent_t *ent); + +static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER(); +static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER(); + +/** Hashtable helper: compare two keypin table entries and return true iff + * they have the same RSA key IDs. */ +static inline int +keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b) +{ + return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id)); +} + +/** Hashtable helper: hash a keypin table entries based on its RSA key ID */ +static inline unsigned +keypin_ent_hash_rsa(const keypin_ent_t *a) +{ +return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id)); +} + +/** Hashtable helper: compare two keypin table entries and return true iff + * they have the same ed25519 keys */ +static inline int +keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b) +{ + return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key)); +} + +/** Hashtable helper: hash a keypin table entries based on its ed25519 key */ +static inline unsigned +keypin_ent_hash_ed(const keypin_ent_t *a) +{ +return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key)); +} + +HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, + keypin_ents_eq_rsa); +HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa, + keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_); + +HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, + keypin_ents_eq_ed); +HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, + keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_); + +/** + * Check whether we already have an entry in the key pinning table for a + * router with RSA ID digest <b>rsa_id_digest</b> or for ed25519 key + * <b>ed25519_id_key</b>. If we have an entry that matches both keys, + * return KEYPIN_FOUND. If we find an entry that matches one key but + * not the other, return KEYPIN_MISMATCH. If we have no entry for either + * key, add such an entry to the table and return KEYPIN_ADDED. + * + * If <b>replace_existing_entry</b> is true, then any time we would have said + * KEYPIN_FOUND, we instead add this entry anyway and return KEYPIN_ADDED. + */ +int +keypin_check_and_add(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + const int replace_existing_entry) +{ + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0, + replace_existing_entry); +} + +/** + * As keypin_check_and_add, but do not add. Return KEYPIN_NOT_FOUND if + * we would add. + */ +int +keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1, 0); +} + +/** + * Helper: implements keypin_check and keypin_check_and_add. + */ +static int +keypin_check_and_add_impl(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + const int do_not_add, + const int replace) +{ + keypin_ent_t search, *ent; + memset(&search, 0, sizeof(search)); + memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id)); + memcpy(search.ed25519_key, ed25519_id_key, sizeof(search.ed25519_key)); + + /* Search by RSA key digest first */ + ent = HT_FIND(rsamap, &the_rsa_map, &search); + if (ent) { + tor_assert(fast_memeq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id))); + if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) { + return KEYPIN_FOUND; /* Match on both keys. Great. */ + } else { + if (!replace) + return KEYPIN_MISMATCH; /* Found RSA with different Ed key */ + } + } + + /* See if we know a different RSA key for this ed key */ + if (! replace) { + ent = HT_FIND(edmap, &the_ed_map, &search); + if (ent) { + /* If we got here, then the ed key matches and the RSA doesn't */ + tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key, + sizeof(ent->ed25519_key))); + tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id))); + return KEYPIN_MISMATCH; + } + } + + /* Okay, this one is new to us. */ + if (do_not_add) + return KEYPIN_NOT_FOUND; + + ent = tor_memdup(&search, sizeof(search)); + int r = keypin_add_or_replace_entry_in_map(ent); + if (! replace) { + tor_assert(r == 1); + } else { + tor_assert(r != 0); + } + keypin_journal_append_entry(rsa_id_digest, ed25519_id_key); + return KEYPIN_ADDED; +} + +/** + * Helper: add <b>ent</b> to the hash tables. + */ +MOCK_IMPL(STATIC void, +keypin_add_entry_to_map, (keypin_ent_t *ent)) +{ + HT_INSERT(rsamap, &the_rsa_map, ent); + HT_INSERT(edmap, &the_ed_map, ent); +} + +/** + * Helper: add 'ent' to the maps, replacing any entries that contradict it. + * Take ownership of 'ent', freeing it if needed. + * + * Return 0 if the entry was a duplicate, -1 if there was a conflict, + * and 1 if there was no conflict. + */ +static int +keypin_add_or_replace_entry_in_map(keypin_ent_t *ent) +{ + int r = 1; + keypin_ent_t *ent2 = HT_FIND(rsamap, &the_rsa_map, ent); + keypin_ent_t *ent3 = HT_FIND(edmap, &the_ed_map, ent); + if (ent2 && + fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) { + /* We already have this mapping stored. Ignore it. */ + tor_free(ent); + return 0; + } else if (ent2 || ent3) { + /* We have a conflict. (If we had no entry, we would have ent2 == ent3 + * == NULL. If we had a non-conflicting duplicate, we would have found + * it above.) + * + * We respond by having this entry (ent) supersede all entries that it + * contradicts (ent2 and/or ent3). In other words, if we receive + * <rsa,ed>, we remove all <rsa,ed'> and all <rsa',ed>, for rsa'!=rsa + * and ed'!= ed. + */ + const keypin_ent_t *t; + if (ent2) { + t = HT_REMOVE(rsamap, &the_rsa_map, ent2); + tor_assert(ent2 == t); + t = HT_REMOVE(edmap, &the_ed_map, ent2); + tor_assert(ent2 == t); + } + if (ent3 && ent2 != ent3) { + t = HT_REMOVE(rsamap, &the_rsa_map, ent3); + tor_assert(ent3 == t); + t = HT_REMOVE(edmap, &the_ed_map, ent3); + tor_assert(ent3 == t); + tor_free(ent3); + } + tor_free(ent2); + r = -1; + /* Fall through */ + } + + keypin_add_entry_to_map(ent); + return r; +} + +/** + * Check whether we already have an entry in the key pinning table for a + * router with RSA ID digest <b>rsa_id_digest</b>. If we have no such entry, + * return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but + * which has an ed25519 key, return KEYPIN_MISMATCH. + */ +int +keypin_check_lone_rsa(const uint8_t *rsa_id_digest) +{ + keypin_ent_t search, *ent; + memset(&search, 0, sizeof(search)); + memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id)); + + /* Search by RSA key digest first */ + ent = HT_FIND(rsamap, &the_rsa_map, &search); + if (ent) { + return KEYPIN_MISMATCH; + } else { + return KEYPIN_NOT_FOUND; + } +} + +/** Open fd to the keypinning journal file. */ +static int keypin_journal_fd = -1; + +/** Open the key-pinning journal to append to <b>fname</b>. Return 0 on + * success, -1 on failure. */ +int +keypin_open_journal(const char *fname) +{ + /* O_SYNC ??*/ + int fd = tor_open_cloexec(fname, O_WRONLY|O_CREAT|O_BINARY, 0600); + if (fd < 0) + goto err; + + if (tor_fd_seekend(fd) < 0) + goto err; + + /* Add a newline in case the last line was only partially written */ + if (write(fd, "\n", 1) < 1) + goto err; + + /* Add something about when we opened this file. */ + char buf[80]; + char tbuf[ISO_TIME_LEN+1]; + format_iso_time(tbuf, approx_time()); + tor_snprintf(buf, sizeof(buf), "@opened-at %s\n", tbuf); + if (write_all(fd, buf, strlen(buf), 0) < 0) + goto err; + + keypin_journal_fd = fd; + return 0; + err: + if (fd >= 0) + close(fd); + return -1; +} + +/** Close the keypinning journal file. */ +int +keypin_close_journal(void) +{ + if (keypin_journal_fd >= 0) + close(keypin_journal_fd); + keypin_journal_fd = -1; + return 0; +} + +/** Length of a keypinning journal line, including terminating newline. */ +#define JOURNAL_LINE_LEN (BASE64_DIGEST_LEN + BASE64_DIGEST256_LEN + 2) + +/** Add an entry to the keypinning journal to map <b>rsa_id_digest</b> and + * <b>ed25519_id_key</b>. */ +static int +keypin_journal_append_entry(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key) +{ + if (keypin_journal_fd == -1) + return -1; + char line[JOURNAL_LINE_LEN]; + digest_to_base64(line, (const char*)rsa_id_digest); + line[BASE64_DIGEST_LEN] = ' '; + digest256_to_base64(line + BASE64_DIGEST_LEN + 1, + (const char*)ed25519_id_key); + line[BASE64_DIGEST_LEN+1+BASE64_DIGEST256_LEN] = '\n'; + + if (write_all(keypin_journal_fd, line, JOURNAL_LINE_LEN, 0)<0) { + log_warn(LD_DIRSERV, "Error while adding a line to the key-pinning " + "journal: %s", strerror(errno)); + keypin_close_journal(); + return -1; + } + + return 0; +} + +/** Load a journal from the <b>size</b>-byte region at <b>data</b>. Return 0 + * on success, -1 on failure. */ +STATIC int +keypin_load_journal_impl(const char *data, size_t size) +{ + const char *start = data, *end = data + size, *next; + + int n_corrupt_lines = 0; + int n_entries = 0; + int n_duplicates = 0; + int n_conflicts = 0; + + for (const char *cp = start; cp < end; cp = next) { + const char *eol = memchr(cp, '\n', end-cp); + const char *eos = eol ? eol : end; + const size_t len = eos - cp; + + next = eol ? eol + 1 : end; + + if (len == 0) { + continue; + } + + if (*cp == '@') { + /* Lines that start with @ are reserved. Ignore for now. */ + continue; + } + if (*cp == '#') { + /* Lines that start with # are comments. */ + continue; + } + + /* Is it the right length? (The -1 here is for the newline.) */ + if (len != JOURNAL_LINE_LEN - 1) { + /* Lines with a bad length are corrupt unless they are empty. + * Ignore them either way */ + for (const char *s = cp; s < eos; ++s) { + if (! TOR_ISSPACE(*s)) { + ++n_corrupt_lines; + break; + } + } + continue; + } + + keypin_ent_t *ent = keypin_parse_journal_line(cp); + + if (ent == NULL) { + ++n_corrupt_lines; + continue; + } + + const int r = keypin_add_or_replace_entry_in_map(ent); + if (r == 0) { + ++n_duplicates; + } else if (r == -1) { + ++n_conflicts; + } + + ++n_entries; + } + + int severity = (n_corrupt_lines || n_duplicates) ? LOG_WARN : LOG_INFO; + tor_log(severity, LD_DIRSERV, + "Loaded %d entries from keypin journal. " + "Found %d corrupt lines, %d duplicates, and %d conflicts.", + n_entries, n_corrupt_lines, n_duplicates, n_conflicts); + + return 0; +} + +/** + * Load a journal from the file called <b>fname</b>. Return 0 on success, + * -1 on failure. + */ +int +keypin_load_journal(const char *fname) +{ + tor_mmap_t *map = tor_mmap_file(fname); + if (!map) { + if (errno == ENOENT) + return 0; + else + return -1; + } + int r = keypin_load_journal_impl(map->data, map->size); + tor_munmap_file(map); + return r; +} + +/** Parse a single keypinning journal line entry from <b>cp</b>. The input + * does not need to be NUL-terminated, but it <em>does</em> need to have + * KEYPIN_JOURNAL_LINE_LEN -1 bytes available to read. Return a new entry + * on success, and NULL on failure. + */ +STATIC keypin_ent_t * +keypin_parse_journal_line(const char *cp) +{ + /* XXXX assumes !USE_OPENSSL_BASE64 */ + keypin_ent_t *ent = tor_malloc_zero(sizeof(keypin_ent_t)); + + if (base64_decode((char*)ent->rsa_id, sizeof(ent->rsa_id), + cp, BASE64_DIGEST_LEN) != DIGEST_LEN || + cp[BASE64_DIGEST_LEN] != ' ' || + base64_decode((char*)ent->ed25519_key, sizeof(ent->ed25519_key), + cp+BASE64_DIGEST_LEN+1, BASE64_DIGEST256_LEN) != DIGEST256_LEN) { + tor_free(ent); + return NULL; + } else { + return ent; + } +} + +/** Remove all entries from the keypinning table.*/ +void +keypin_clear(void) +{ + int bad_entries = 0; + { + keypin_ent_t **ent, **next, *this; + for (ent = HT_START(rsamap, &the_rsa_map); ent != NULL; ent = next) { + this = *ent; + next = HT_NEXT_RMV(rsamap, &the_rsa_map, ent); + + keypin_ent_t *other_ent = HT_REMOVE(edmap, &the_ed_map, this); + bad_entries += (other_ent != this); + + tor_free(this); + } + } + bad_entries += HT_SIZE(&the_ed_map); + + HT_CLEAR(edmap,&the_ed_map); + HT_CLEAR(rsamap,&the_rsa_map); + + if (bad_entries) { + log_warn(LD_BUG, "Found %d discrepencies in the the keypin database.", + bad_entries); + } +} + diff --git a/src/or/keypin.h b/src/or/keypin.h new file mode 100644 index 0000000000..673f24d9e3 --- /dev/null +++ b/src/or/keypin.h @@ -0,0 +1,47 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_KEYPIN_H +#define TOR_KEYPIN_H + +#include "testsupport.h" + +int keypin_check_and_add(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key, + const int replace_existing_entry); +int keypin_check(const uint8_t *rsa_id_digest, + const uint8_t *ed25519_id_key); + +int keypin_open_journal(const char *fname); +int keypin_close_journal(void); +int keypin_load_journal(const char *fname); +void keypin_clear(void); +int keypin_check_lone_rsa(const uint8_t *rsa_id_digest); + +#define KEYPIN_FOUND 0 +#define KEYPIN_ADDED 1 +#define KEYPIN_MISMATCH -1 +#define KEYPIN_NOT_FOUND -2 + +#ifdef KEYPIN_PRIVATE + +/** + * In-memory representation of a key-pinning table entry. + */ +typedef struct keypin_ent_st { + HT_ENTRY(keypin_ent_st) rsamap_node; + HT_ENTRY(keypin_ent_st) edmap_node; + /** SHA1 hash of the RSA key */ + uint8_t rsa_id[DIGEST_LEN]; + /** Ed2219 key. */ + uint8_t ed25519_key[DIGEST256_LEN]; +} keypin_ent_t; + +STATIC keypin_ent_t * keypin_parse_journal_line(const char *cp); +STATIC int keypin_load_journal_impl(const char *data, size_t size); + +MOCK_DECL(STATIC void, keypin_add_entry_to_map, (keypin_ent_t *ent)); +#endif + +#endif + diff --git a/src/or/main.c b/src/or/main.c index df0cd1bee3..6b5619c7d6 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -37,12 +37,14 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "keypin.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" #include "ntmain.h" #include "onion.h" +#include "periodic.h" #include "policies.h" #include "transports.h" #include "relay.h" @@ -51,6 +53,7 @@ #include "rendservice.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" @@ -63,7 +66,7 @@ #include <openssl/crypto.h> #endif #include "memarea.h" -#include "../common/sandbox.h" +#include "sandbox.h" #ifdef HAVE_EVENT2_EVENT_H #include <event2/event.h> @@ -98,6 +101,7 @@ static int conn_close_if_marked(int i); static void connection_start_reading_from_linked_conn(connection_t *conn); static int connection_should_read_from_linked_conn(connection_t *conn); static int run_main_loop_until_done(void); +static void process_signal(int sig); /********* START VARIABLES **********/ @@ -131,8 +135,6 @@ static uint64_t stats_n_bytes_written = 0; time_t time_of_process_start = 0; /** How many seconds have we been running? */ long stats_n_seconds_working = 0; -/** When do we next launch DNS wildcarding checks? */ -static time_t time_to_check_for_correct_dns = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -189,32 +191,6 @@ int quiet_level = 0; * ****************************************************************************/ -#if 0 && defined(USE_BUFFEREVENTS) -static void -free_old_inbuf(connection_t *conn) -{ - if (! conn->inbuf) - return; - - tor_assert(conn->outbuf); - tor_assert(buf_datalen(conn->inbuf) == 0); - tor_assert(buf_datalen(conn->outbuf) == 0); - buf_free(conn->inbuf); - buf_free(conn->outbuf); - conn->inbuf = conn->outbuf = NULL; - - if (conn->read_event) { - event_del(conn->read_event); - tor_event_free(conn->read_event); - } - if (conn->write_event) { - event_del(conn->read_event); - tor_event_free(conn->write_event); - } - conn->read_event = conn->write_event = NULL; -} -#endif - #if defined(_WIN32) && defined(USE_BUFFEREVENTS) /** Remove the kernel-space send and receive buffers for <b>s</b>. For use * with IOCP only. */ @@ -223,11 +199,13 @@ set_buffer_lengths_to_zero(tor_socket_t s) { int zero = 0; int r = 0; - if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, sizeof(zero))) { + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (void*)&zero, + (socklen_t)sizeof(zero))) { log_warn(LD_NET, "Unable to clear SO_SNDBUF"); r = -1; } - if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, sizeof(zero))) { + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (void*)&zero, + (socklen_t)sizeof(zero))) { log_warn(LD_NET, "Unable to clear SO_RCVBUF"); r = -1; } @@ -498,8 +476,7 @@ connection_in_array(connection_t *conn) return smartlist_contains(connection_array, conn); } -/** Set <b>*array</b> to an array of all connections, and <b>*n</b> - * to the length of the array. <b>*array</b> and <b>*n</b> must not +/** Set <b>*array</b> to an array of all connections. <b>*array</b> must not * be modified. */ smartlist_t * @@ -566,6 +543,46 @@ connection_is_reading(connection_t *conn) (conn->read_event && event_pending(conn->read_event, EV_READ, NULL)); } +/** Check whether <b>conn</b> is correct in having (or not having) a + * read/write event (passed in <b>ev</b>). On success, return 0. On failure, + * log a warning and return -1. */ +static int +connection_check_event(connection_t *conn, struct event *ev) +{ + int bad; + + if (conn->type == CONN_TYPE_AP && TO_EDGE_CONN(conn)->is_dns_request) { + /* DNS requests which we launch through the dnsserv.c module do not have + * any underlying socket or any underlying linked connection, so they + * shouldn't have any attached events either. + */ + bad = ev != NULL; + } else { + /* Everytyhing else should have an underlying socket, or a linked + * connection (which is also tracked with a read_event/write_event pair). + */ + bad = ev == NULL; + } + + if (bad) { + log_warn(LD_BUG, "Event missing on connection %p [%s;%s]. " + "socket=%d. linked=%d. " + "is_dns_request=%d. Marked_for_close=%s:%d", + conn, + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + (int)conn->s, (int)conn->linked, + (conn->type == CONN_TYPE_AP && + TO_EDGE_CONN(conn)->is_dns_request), + conn->marked_for_close_file ? conn->marked_for_close_file : "-", + conn->marked_for_close + ); + log_backtrace(LOG_WARN, LD_BUG, "Backtrace attached."); + return -1; + } + return 0; +} + /** Tell the main loop to stop notifying <b>conn</b> of any read events. */ MOCK_IMPL(void, connection_stop_reading,(connection_t *conn)) @@ -577,7 +594,9 @@ connection_stop_reading,(connection_t *conn)) return; }); - tor_assert(conn->read_event); + if (connection_check_event(conn, conn->read_event) < 0) { + return; + } if (conn->linked) { conn->reading_from_linked_conn = 0; @@ -602,7 +621,9 @@ connection_start_reading,(connection_t *conn)) return; }); - tor_assert(conn->read_event); + if (connection_check_event(conn, conn->read_event) < 0) { + return; + } if (conn->linked) { conn->reading_from_linked_conn = 1; @@ -642,7 +663,9 @@ connection_stop_writing,(connection_t *conn)) return; }); - tor_assert(conn->write_event); + if (connection_check_event(conn, conn->write_event) < 0) { + return; + } if (conn->linked) { conn->writing_to_linked_conn = 0; @@ -668,7 +691,9 @@ connection_start_writing,(connection_t *conn)) return; }); - tor_assert(conn->write_event); + if (connection_check_event(conn, conn->write_event) < 0) { + return; + } if (conn->linked) { conn->writing_to_linked_conn = 1; @@ -943,18 +968,6 @@ conn_close_if_marked(int i) * would make much more sense to react in * connection_handle_read_impl, or to just stop reading in * mark_and_flush */ -#if 0 -#define MARKED_READING_RATE 180 - static ratelim_t marked_read_lim = RATELIM_INIT(MARKED_READING_RATE); - char *m; - if ((m = rate_limit_log(&marked_read_lim, now))) { - log_warn(LD_BUG, "Marked connection (fd %d, type %s, state %s) " - "is still reading; that shouldn't happen.%s", - (int)conn->s, conn_type_to_string(conn->type), - conn_state_to_string(conn->type, conn->state), m); - tor_free(m); - } -#endif conn->read_blocked_on_bw = 1; connection_stop_reading(conn); } @@ -982,19 +995,18 @@ conn_close_if_marked(int i) return 1; } -/** We've just tried every dirserver we know about, and none of - * them were reachable. Assume the network is down. Change state - * so next time an application connection arrives we'll delay it - * and try another directory fetch. Kill off all the circuit_wait - * streams that are waiting now, since they will all timeout anyway. +/** Implementation for directory_all_unreachable. This is done in a callback, + * since otherwise it would complicate Tor's control-flow graph beyond all + * reason. */ -void -directory_all_unreachable(time_t now) +static void +directory_all_unreachable_cb(evutil_socket_t fd, short event, void *arg) { - connection_t *conn; - (void)now; + (void)fd; + (void)event; + (void)arg; - stats_n_seconds_working=0; /* reset it */ + connection_t *conn; while ((conn = connection_get_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT))) { @@ -1007,18 +1019,43 @@ directory_all_unreachable(time_t now) connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_NET_UNREACHABLE); } - control_event_general_status(LOG_ERR, "DIR_ALL_UNREACHABLE"); + control_event_general_error("DIR_ALL_UNREACHABLE"); +} + +static struct event *directory_all_unreachable_cb_event = NULL; + +/** We've just tried every dirserver we know about, and none of + * them were reachable. Assume the network is down. Change state + * so next time an application connection arrives we'll delay it + * and try another directory fetch. Kill off all the circuit_wait + * streams that are waiting now, since they will all timeout anyway. + */ +void +directory_all_unreachable(time_t now) +{ + (void)now; + + stats_n_seconds_working=0; /* reset it */ + + if (!directory_all_unreachable_cb_event) { + directory_all_unreachable_cb_event = + tor_event_new(tor_libevent_get_base(), + -1, EV_READ, directory_all_unreachable_cb, NULL); + tor_assert(directory_all_unreachable_cb_event); + } + + event_active(directory_all_unreachable_cb_event, EV_READ, 1); } /** This function is called whenever we successfully pull down some new * network statuses or server descriptors. */ void -directory_info_has_arrived(time_t now, int from_cache) +directory_info_has_arrived(time_t now, int from_cache, int suppress_logs) { const or_options_t *options = get_options(); if (!router_have_minimum_dir_info()) { - int quiet = from_cache || + int quiet = suppress_logs || from_cache || directory_too_idle_to_fetch_descriptors(options, now); tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR, "I learned some more directory information, but not enough to " @@ -1202,7 +1239,165 @@ get_signewnym_epoch(void) return newnym_epoch; } -static time_t time_to_check_descriptor = 0; +/** True iff we have initialized all the members of <b>periodic_events</b>. + * Used to prevent double-initialization. */ +static int periodic_events_initialized = 0; + +/* Declare all the timer callback functions... */ +#undef CALLBACK +#define CALLBACK(name) \ + static int name ## _callback(time_t, const or_options_t *) +CALLBACK(rotate_onion_key); +CALLBACK(check_ed_keys); +CALLBACK(launch_descriptor_fetches); +CALLBACK(reset_descriptor_failures); +CALLBACK(rotate_x509_certificate); +CALLBACK(add_entropy); +CALLBACK(launch_reachability_tests); +CALLBACK(downrate_stability); +CALLBACK(save_stability); +CALLBACK(check_authority_cert); +CALLBACK(check_expired_networkstatus); +CALLBACK(write_stats_file); +CALLBACK(record_bridge_stats); +CALLBACK(clean_caches); +CALLBACK(rend_cache_failure_clean); +CALLBACK(retry_dns); +CALLBACK(check_descriptor); +CALLBACK(check_for_reachability_bw); +CALLBACK(fetch_networkstatus); +CALLBACK(retry_listeners); +CALLBACK(expire_old_ciruits_serverside); +CALLBACK(check_dns_honesty); +CALLBACK(write_bridge_ns); +CALLBACK(check_fw_helper_app); +CALLBACK(heartbeat); + +#undef CALLBACK + +/* Now we declare an array of periodic_event_item_t for each periodic event */ +#define CALLBACK(name) PERIODIC_EVENT(name) + +static periodic_event_item_t periodic_events[] = { + CALLBACK(rotate_onion_key), + CALLBACK(check_ed_keys), + CALLBACK(launch_descriptor_fetches), + CALLBACK(reset_descriptor_failures), + CALLBACK(rotate_x509_certificate), + CALLBACK(add_entropy), + CALLBACK(launch_reachability_tests), + CALLBACK(downrate_stability), + CALLBACK(save_stability), + CALLBACK(check_authority_cert), + CALLBACK(check_expired_networkstatus), + CALLBACK(write_stats_file), + CALLBACK(record_bridge_stats), + CALLBACK(clean_caches), + CALLBACK(rend_cache_failure_clean), + CALLBACK(retry_dns), + CALLBACK(check_descriptor), + CALLBACK(check_for_reachability_bw), + CALLBACK(fetch_networkstatus), + CALLBACK(retry_listeners), + CALLBACK(expire_old_ciruits_serverside), + CALLBACK(check_dns_honesty), + CALLBACK(write_bridge_ns), + CALLBACK(check_fw_helper_app), + CALLBACK(heartbeat), + END_OF_PERIODIC_EVENTS +}; +#undef CALLBACK + +/* These are pointers to members of periodic_events[] that are used to + * implement particular callbacks. We keep them separate here so that we + * can access them by name. We also keep them inside periodic_events[] + * so that we can implement "reset all timers" in a reasonable way. */ +static periodic_event_item_t *check_descriptor_event=NULL; +static periodic_event_item_t *fetch_networkstatus_event=NULL; +static periodic_event_item_t *launch_descriptor_fetches_event=NULL; +static periodic_event_item_t *check_dns_honesty_event=NULL; + +/** Reset all the periodic events so we'll do all our actions again as if we + * just started up. + * Useful if our clock just moved back a long time from the future, + * so we don't wait until that future arrives again before acting. + */ +void +reset_all_main_loop_timers(void) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_reschedule(&periodic_events[i]); + } +} + +/** Return the member of periodic_events[] whose name is <b>name</b>. + * Return NULL if no such event is found. + */ +static periodic_event_item_t * +find_periodic_event(const char *name) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + if (strcmp(name, periodic_events[i].name) == 0) + return &periodic_events[i]; + } + return NULL; +} + +/** Helper, run one second after setup: + * Initializes all members of periodic_events and starts them running. + * + * (We do this one second after setup for backward-compatibility reasons; + * it might not actually be necessary.) */ +static void +initialize_periodic_events_cb(evutil_socket_t fd, short events, void *data) +{ + (void) fd; + (void) events; + (void) data; + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_launch(&periodic_events[i]); + } +} + +/** Set up all the members of periodic_events[], and configure them all to be + * launched from a callback. */ +STATIC void +initialize_periodic_events(void) +{ + tor_assert(periodic_events_initialized == 0); + periodic_events_initialized = 1; + + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_setup(&periodic_events[i]); + } + +#define NAMED_CALLBACK(name) \ + STMT_BEGIN name ## _event = find_periodic_event( #name ); STMT_END + + NAMED_CALLBACK(check_descriptor); + NAMED_CALLBACK(fetch_networkstatus); + NAMED_CALLBACK(launch_descriptor_fetches); + NAMED_CALLBACK(check_dns_honesty); + + struct timeval one_second = { 1, 0 }; + event_base_once(tor_libevent_get_base(), -1, EV_TIMEOUT, + initialize_periodic_events_cb, NULL, + &one_second); +} + +STATIC void +teardown_periodic_events(void) +{ + int i; + for (i = 0; periodic_events[i].name; ++i) { + periodic_event_destroy(&periodic_events[i]); + } +} + /** * Update our schedule so that we'll check whether we need to update our * descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL @@ -1211,7 +1406,45 @@ static time_t time_to_check_descriptor = 0; void reschedule_descriptor_update_check(void) { - time_to_check_descriptor = 0; + tor_assert(check_descriptor_event); + periodic_event_reschedule(check_descriptor_event); +} + +/** + * Update our schedule so that we'll check whether we need to fetch directory + * info immediately. + */ +void +reschedule_directory_downloads(void) +{ + tor_assert(fetch_networkstatus_event); + tor_assert(launch_descriptor_fetches_event); + + periodic_event_reschedule(fetch_networkstatus_event); + periodic_event_reschedule(launch_descriptor_fetches_event); +} + +#define LONGEST_TIMER_PERIOD (30 * 86400) +/** Helper: Return the number of seconds between <b>now</b> and <b>next</b>, + * clipped to the range [1 second, LONGEST_TIMER_PERIOD]. */ +static inline int +safe_timer_diff(time_t now, time_t next) +{ + if (next > now) { + /* There were no computers at signed TIME_MIN (1902 on 32-bit systems), + * and nothing that could run Tor. It's a bug if 'next' is around then. + * On 64-bit systems with signed TIME_MIN, TIME_MIN is before the Big + * Bang. We cannot extrapolate past a singularity, but there was probably + * nothing that could run Tor then, either. + **/ + tor_assert(next > TIME_MIN + LONGEST_TIMER_PERIOD); + + if (next - LONGEST_TIMER_PERIOD > now) + return LONGEST_TIMER_PERIOD; + return (int)(next - now); + } else { + return 1; + } } /** Perform regular maintenance tasks. This function gets run once per @@ -1220,32 +1453,8 @@ reschedule_descriptor_update_check(void) static void run_scheduled_events(time_t now) { - static time_t last_rotated_x509_certificate = 0; - static time_t time_to_check_v3_certificate = 0; - static time_t time_to_check_listeners = 0; - static time_t time_to_download_networkstatus = 0; - static time_t time_to_try_getting_descriptors = 0; - static time_t time_to_reset_descriptor_failures = 0; - static time_t time_to_add_entropy = 0; - static time_t time_to_write_bridge_status_file = 0; - static time_t time_to_downrate_stability = 0; - static time_t time_to_save_stability = 0; - static time_t time_to_clean_caches = 0; - static time_t time_to_recheck_bandwidth = 0; - static time_t time_to_check_for_expired_networkstatus = 0; - static time_t time_to_write_stats_files = 0; - static time_t time_to_write_bridge_stats = 0; - static time_t time_to_check_port_forwarding = 0; - static time_t time_to_launch_reachability_tests = 0; - static int should_init_bridge_stats = 1; - static time_t time_to_retry_dns_init = 0; - static time_t time_to_next_heartbeat = 0; const or_options_t *options = get_options(); - int is_server = server_mode(options); - int i; - int have_dir_info; - /* 0. See if we've been asked to shut down and our timeout has * expired; or if our bandwidth limits are exhausted and we * should hibernate; or if it's time to wake up from hibernation. @@ -1263,12 +1472,98 @@ run_scheduled_events(time_t now) /* 0c. If we've deferred log messages for the controller, handle them now */ flush_pending_log_callbacks(); + if (options->UseBridges && !options->DisableNetwork) { + fetch_bridge_descriptors(options, now); + } + + if (accounting_is_enabled(options)) { + accounting_run_housekeeping(now); + } + + if (authdir_mode_v3(options)) { + dirvote_act(options, now); + } + + /* 3a. Every second, we examine pending circuits and prune the + * ones which have been pending for more than a few seconds. + * We do this before step 4, so it can try building more if + * it's not comfortable with the number of available circuits. + */ + /* (If our circuit build timeout can ever become lower than a second (which + * it can't, currently), we should do this more often.) */ + circuit_expire_building(); + + /* 3b. Also look at pending streams and prune the ones that 'began' + * a long time ago but haven't gotten a 'connected' yet. + * Do this before step 4, so we can put them back into pending + * state to be picked up by the new circuit. + */ + connection_ap_expire_beginning(); + + /* 3c. And expire connections that we've held open for too long. + */ + connection_expire_held_open(); + + /* 4. Every second, we try a new circuit if there are no valid + * circuits. Every NewCircuitPeriod seconds, we expire circuits + * that became dirty more than MaxCircuitDirtiness seconds ago, + * and we make a new circ if there are no clean circuits. + */ + const int have_dir_info = router_have_minimum_dir_info(); + if (have_dir_info && !net_is_disabled()) { + circuit_build_needed_circs(now); + } else { + circuit_expire_old_circs_as_needed(now); + } + + /* 5. We do housekeeping for each connection... */ + connection_or_set_bad_connections(NULL, 0); + int i; + for (i=0;i<smartlist_len(connection_array);i++) { + run_connection_housekeeping(i, now); + } + + /* 6. And remove any marked circuits... */ + circuit_close_all_marked(); + + /* 7. And upload service descriptors if necessary. */ + if (have_completed_a_circuit() && !net_is_disabled()) { + rend_consider_services_upload(now); + rend_consider_descriptor_republication(); + } + + /* 8. and blow away any connections that need to die. have to do this now, + * because if we marked a conn for close and left its socket -1, then + * we'll pass it to poll/select and bad things will happen. + */ + close_closeable_connections(); + + /* 8b. And if anything in our state is ready to get flushed to disk, we + * flush it. */ + or_state_save(now); + + /* 8c. Do channel cleanup just like for connections */ + channel_run_cleanup(); + channel_listener_run_cleanup(); + + /* 11b. check pending unconfigured managed proxies */ + if (!net_is_disabled() && pt_proxies_configuration_pending()) + pt_configure_remaining_proxies(); +} + +static int +rotate_onion_key_callback(time_t now, const or_options_t *options) +{ /* 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys, * shut down and restart all cpuworkers, and update the directory if * necessary. */ - if (is_server && - get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) { + if (server_mode(options)) { + time_t rotation_time = get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME; + if (rotation_time > now) { + return safe_timer_diff(now, rotation_time); + } + log_info(LD_GENERAL,"Rotating onion key."); rotate_onion_key(); cpuworkers_rotate_keyinfo(); @@ -1277,193 +1572,272 @@ run_scheduled_events(time_t now) } if (advertised_server_mode() && !options->DisableNetwork) router_upload_dir_desc_to_dirservers(0); + return MIN_ONION_KEY_LIFETIME; } + return PERIODIC_EVENT_NO_UPDATE; +} - if (!should_delay_dir_fetches(options, NULL) && - time_to_try_getting_descriptors < now) { - update_all_descriptor_downloads(now); - update_extrainfo_downloads(now); - if (router_have_minimum_dir_info()) - time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL; - else - time_to_try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL; +static int +check_ed_keys_callback(time_t now, const or_options_t *options) +{ + if (server_mode(options)) { + if (should_make_new_ed_keys(options, now)) { + if (load_ed_keys(options, now) < 0 || + generate_ed_link_cert(options, now)) { + log_err(LD_OR, "Unable to update Ed25519 keys! Exiting."); + tor_cleanup(); + exit(0); + } + } + return 30; } + return PERIODIC_EVENT_NO_UPDATE; +} - if (time_to_reset_descriptor_failures < now) { - router_reset_descriptor_download_failures(); - time_to_reset_descriptor_failures = - now + DESCRIPTOR_FAILURE_RESET_INTERVAL; - } +static int +launch_descriptor_fetches_callback(time_t now, const or_options_t *options) +{ + if (should_delay_dir_fetches(options, NULL)) + return PERIODIC_EVENT_NO_UPDATE; - if (options->UseBridges && !options->DisableNetwork) - fetch_bridge_descriptors(options, now); + update_all_descriptor_downloads(now); + update_extrainfo_downloads(now); + if (router_have_minimum_dir_info()) + return LAZY_DESCRIPTOR_RETRY_INTERVAL; + else + return GREEDY_DESCRIPTOR_RETRY_INTERVAL; +} + +static int +reset_descriptor_failures_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + router_reset_descriptor_download_failures(); + return DESCRIPTOR_FAILURE_RESET_INTERVAL; +} + +static int +rotate_x509_certificate_callback(time_t now, const or_options_t *options) +{ + static int first = 1; + (void)now; + (void)options; + if (first) { + first = 0; + return MAX_SSL_KEY_LIFETIME_INTERNAL; + } /* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our * TLS context. */ - if (!last_rotated_x509_certificate) - last_rotated_x509_certificate = now; - if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) { - log_info(LD_GENERAL,"Rotating tls context."); - if (router_initialize_tls_context() < 0) { - log_warn(LD_BUG, "Error reinitializing TLS context"); - /* XXX is it a bug here, that we just keep going? -RD */ - } - last_rotated_x509_certificate = now; - /* We also make sure to rotate the TLS connections themselves if they've - * been up for too long -- but that's done via is_bad_for_new_circs in - * connection_run_housekeeping() above. */ + log_info(LD_GENERAL,"Rotating tls context."); + if (router_initialize_tls_context() < 0) { + log_warn(LD_BUG, "Error reinitializing TLS context"); + tor_assert(0); } - if (time_to_add_entropy < now) { - if (time_to_add_entropy) { - /* We already seeded once, so don't die on failure. */ - crypto_seed_rng(0); - } -/** How often do we add more entropy to OpenSSL's RNG pool? */ -#define ENTROPY_INTERVAL (60*60) - time_to_add_entropy = now + ENTROPY_INTERVAL; + /* We also make sure to rotate the TLS connections themselves if they've + * been up for too long -- but that's done via is_bad_for_new_circs in + * run_connection_housekeeping() above. */ + return MAX_SSL_KEY_LIFETIME_INTERNAL; +} + +static int +add_entropy_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; + /* We already seeded once, so don't die on failure. */ + if (crypto_seed_rng() < 0) { + log_warn(LD_GENERAL, "Tried to re-seed RNG, but failed. We already " + "seeded once, though, so we won't exit here."); } - /* 1c. If we have to change the accounting interval or record - * bandwidth used in this accounting interval, do so. */ - if (accounting_is_enabled(options)) - accounting_run_housekeeping(now); + /** How often do we add more entropy to OpenSSL's RNG pool? */ +#define ENTROPY_INTERVAL (60*60) + return ENTROPY_INTERVAL; +} - if (time_to_launch_reachability_tests < now && - (authdir_mode_tests_reachability(options)) && - !net_is_disabled()) { - time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; +static int +launch_reachability_tests_callback(time_t now, const or_options_t *options) +{ + if (authdir_mode_tests_reachability(options) && + !net_is_disabled()) { /* try to determine reachability of the other Tor relays */ dirserv_test_reachability(now); } + return REACHABILITY_TEST_INTERVAL; +} +static int +downrate_stability_callback(time_t now, const or_options_t *options) +{ + (void)options; /* 1d. Periodically, we discount older stability information so that new * stability info counts more, and save the stability information to disk as * appropriate. */ - if (time_to_downrate_stability < now) - time_to_downrate_stability = rep_hist_downrate_old_runs(now); + time_t next = rep_hist_downrate_old_runs(now); + return safe_timer_diff(now, next); +} + +static int +save_stability_callback(time_t now, const or_options_t *options) +{ if (authdir_mode_tests_reachability(options)) { - if (time_to_save_stability < now) { - if (time_to_save_stability && rep_hist_record_mtbf_data(now, 1)<0) { - log_warn(LD_GENERAL, "Couldn't store mtbf data."); - } -#define SAVE_STABILITY_INTERVAL (30*60) - time_to_save_stability = now + SAVE_STABILITY_INTERVAL; + if (rep_hist_record_mtbf_data(now, 1)<0) { + log_warn(LD_GENERAL, "Couldn't store mtbf data."); } } +#define SAVE_STABILITY_INTERVAL (30*60) + return SAVE_STABILITY_INTERVAL; +} +static int +check_authority_cert_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; /* 1e. Periodically, if we're a v3 authority, we check whether our cert is * close to expiring and warn the admin if it is. */ - if (time_to_check_v3_certificate < now) { - v3_authority_check_key_expiry(); + v3_authority_check_key_expiry(); #define CHECK_V3_CERTIFICATE_INTERVAL (5*60) - time_to_check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL; - } + return CHECK_V3_CERTIFICATE_INTERVAL; +} +static int +check_expired_networkstatus_callback(time_t now, const or_options_t *options) +{ + (void)options; /* 1f. Check whether our networkstatus has expired. */ - if (time_to_check_for_expired_networkstatus < now) { - networkstatus_t *ns = networkstatus_get_latest_consensus(); - /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in - * networkstatus_get_reasonably_live_consensus(), but that value is way - * way too high. Arma: is the bridge issue there resolved yet? -NM */ + networkstatus_t *ns = networkstatus_get_latest_consensus(); + /*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in + * networkstatus_get_reasonably_live_consensus(), but that value is way + * way too high. Arma: is the bridge issue there resolved yet? -NM */ #define NS_EXPIRY_SLOP (24*60*60) - if (ns && ns->valid_until < now+NS_EXPIRY_SLOP && - router_have_minimum_dir_info()) { - router_dir_info_changed(); - } -#define CHECK_EXPIRED_NS_INTERVAL (2*60) - time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL; + if (ns && ns->valid_until < now+NS_EXPIRY_SLOP && + router_have_minimum_dir_info()) { + router_dir_info_changed(); } +#define CHECK_EXPIRED_NS_INTERVAL (2*60) + return CHECK_EXPIRED_NS_INTERVAL; +} +static int +write_stats_file_callback(time_t now, const or_options_t *options) +{ /* 1g. Check whether we should write statistics to disk. */ - if (time_to_write_stats_files < now) { #define CHECK_WRITE_STATS_INTERVAL (60*60) - time_t next_time_to_write_stats_files = (time_to_write_stats_files > 0 ? - time_to_write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL; - if (options->CellStatistics) { - time_t next_write = - rep_hist_buffer_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->DirReqStatistics) { - time_t next_write = geoip_dirreq_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->EntryStatistics) { - time_t next_write = geoip_entry_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->HiddenServiceStatistics) { - time_t next_write = rep_hist_hs_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->ExitPortStatistics) { - time_t next_write = rep_hist_exit_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->ConnDirectionStatistics) { - time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - if (options->BridgeAuthoritativeDir) { - time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files); - if (next_write && next_write < next_time_to_write_stats_files) - next_time_to_write_stats_files = next_write; - } - time_to_write_stats_files = next_time_to_write_stats_files; + time_t next_time_to_write_stats_files = now + CHECK_WRITE_STATS_INTERVAL; + if (options->CellStatistics) { + time_t next_write = + rep_hist_buffer_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->DirReqStatistics) { + time_t next_write = geoip_dirreq_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->EntryStatistics) { + time_t next_write = geoip_entry_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->HiddenServiceStatistics) { + time_t next_write = rep_hist_hs_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->ExitPortStatistics) { + time_t next_write = rep_hist_exit_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->ConnDirectionStatistics) { + time_t next_write = rep_hist_conn_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + } + if (options->BridgeAuthoritativeDir) { + time_t next_write = rep_hist_desc_stats_write(now); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; } + return safe_timer_diff(now, next_time_to_write_stats_files); +} + +static int +record_bridge_stats_callback(time_t now, const or_options_t *options) +{ + static int should_init_bridge_stats = 1; + /* 1h. Check whether we should write bridge statistics to disk. */ if (should_record_bridge_info(options)) { - if (time_to_write_bridge_stats < now) { - if (should_init_bridge_stats) { - /* (Re-)initialize bridge statistics. */ + if (should_init_bridge_stats) { + /* (Re-)initialize bridge statistics. */ geoip_bridge_stats_init(now); - time_to_write_bridge_stats = now + WRITE_STATS_INTERVAL; should_init_bridge_stats = 0; - } else { - /* Possibly write bridge statistics to disk and ask when to write - * them next time. */ - time_to_write_bridge_stats = geoip_bridge_stats_write( - time_to_write_bridge_stats); - } + return WRITE_STATS_INTERVAL; + } else { + /* Possibly write bridge statistics to disk and ask when to write + * them next time. */ + time_t next = geoip_bridge_stats_write(now); + return safe_timer_diff(now, next); } } else if (!should_init_bridge_stats) { /* Bridge mode was turned off. Ensure that stats are re-initialized * next time bridge mode is turned on. */ should_init_bridge_stats = 1; } + return PERIODIC_EVENT_NO_UPDATE; +} +static int +clean_caches_callback(time_t now, const or_options_t *options) +{ /* Remove old information from rephist and the rend cache. */ - if (time_to_clean_caches < now) { - rep_history_clean(now - options->RephistTrackTime); - rend_cache_clean(now); - rend_cache_clean_v2_descs_as_dir(now, 0); - microdesc_cache_rebuild(NULL, 0); + rep_history_clean(now - options->RephistTrackTime); + rend_cache_clean(now, REND_CACHE_TYPE_CLIENT); + rend_cache_clean(now, REND_CACHE_TYPE_SERVICE); + rend_cache_clean_v2_descs_as_dir(now, 0); + microdesc_cache_rebuild(NULL, 0); #define CLEAN_CACHES_INTERVAL (30*60) - time_to_clean_caches = now + CLEAN_CACHES_INTERVAL; - } + return CLEAN_CACHES_INTERVAL; +} +static int +rend_cache_failure_clean_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* We don't keep entries that are more than five minutes old so we try to + * clean it as soon as we can since we want to make sure the client waits + * as little as possible for reachability reasons. */ + rend_cache_failure_clean(now); + return 30; +} + +static int +retry_dns_callback(time_t now, const or_options_t *options) +{ + (void)now; #define RETRY_DNS_INTERVAL (10*60) /* If we're a server and initializing dns failed, retry periodically. */ - if (time_to_retry_dns_init < now) { - time_to_retry_dns_init = now + RETRY_DNS_INTERVAL; - if (is_server && has_dns_init_failed()) - dns_init(); - } + if (server_mode(options) && has_dns_init_failed()) + dns_init(); + return RETRY_DNS_INTERVAL; +} /* 2. Periodically, we consider force-uploading our descriptor * (if we've passed our internal checks). */ +static int +check_descriptor_callback(time_t now, const or_options_t *options) +{ /** How often do we check whether part of our router info has changed in a * way that would require an upload? That includes checking whether our IP * address has changed. */ @@ -1471,184 +1845,184 @@ run_scheduled_events(time_t now) /* 2b. Once per minute, regenerate and upload the descriptor if the old * one is inaccurate. */ - if (time_to_check_descriptor < now && !options->DisableNetwork) { - static int dirport_reachability_count = 0; - time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; + if (!options->DisableNetwork) { check_descriptor_bandwidth_changed(now); check_descriptor_ipaddress_changed(now); mark_my_descriptor_dirty_if_too_old(now); consider_publishable_server(0); - /* also, check religiously for reachability, if it's within the first - * 20 minutes of our uptime. */ - if (is_server && - (have_completed_a_circuit() || !any_predicted_circuits(now)) && - !we_are_hibernating()) { - if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { - consider_testing_reachability(1, dirport_reachability_count==0); - if (++dirport_reachability_count > 5) - dirport_reachability_count = 0; - } else if (time_to_recheck_bandwidth < now) { - /* If we haven't checked for 12 hours and our bandwidth estimate is - * low, do another bandwidth test. This is especially important for - * bridges, since they might go long periods without much use. */ - const routerinfo_t *me = router_get_my_routerinfo(); - if (time_to_recheck_bandwidth && me && - me->bandwidthcapacity < me->bandwidthrate && - me->bandwidthcapacity < 51200) { - reset_bandwidth_test(); - } -#define BANDWIDTH_RECHECK_INTERVAL (12*60*60) - time_to_recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL; - } - } - /* If any networkstatus documents are no longer recent, we need to * update all the descriptors' running status. */ /* Remove dead routers. */ + /* XXXX This doesn't belong here, but it was here in the pre- + * XXXX refactoring code. */ routerlist_remove_old_routers(); } - /* 2c. Every minute (or every second if TestingTorNetwork), check - * whether we want to download any networkstatus documents. */ + return CHECK_DESCRIPTOR_INTERVAL; +} -/* How often do we check whether we should download network status - * documents? */ -#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60) +static int +check_for_reachability_bw_callback(time_t now, const or_options_t *options) +{ + /* XXXX This whole thing was stuck in the middle of what is now + * XXXX check_descriptor_callback. I'm not sure it's right. */ - if (!should_delay_dir_fetches(options, NULL) && - time_to_download_networkstatus < now) { - time_to_download_networkstatus = - now + networkstatus_dl_check_interval(options); - update_networkstatus_downloads(now); + static int dirport_reachability_count = 0; + /* also, check religiously for reachability, if it's within the first + * 20 minutes of our uptime. */ + if (server_mode(options) && + (have_completed_a_circuit() || !any_predicted_circuits(now)) && + !we_are_hibernating()) { + if (stats_n_seconds_working < TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { + consider_testing_reachability(1, dirport_reachability_count==0); + if (++dirport_reachability_count > 5) + dirport_reachability_count = 0; + return 1; + } else { + /* If we haven't checked for 12 hours and our bandwidth estimate is + * low, do another bandwidth test. This is especially important for + * bridges, since they might go long periods without much use. */ + const routerinfo_t *me = router_get_my_routerinfo(); + static int first_time = 1; + if (!first_time && me && + me->bandwidthcapacity < me->bandwidthrate && + me->bandwidthcapacity < 51200) { + reset_bandwidth_test(); + } + first_time = 0; +#define BANDWIDTH_RECHECK_INTERVAL (12*60*60) + return BANDWIDTH_RECHECK_INTERVAL; + } } + return CHECK_DESCRIPTOR_INTERVAL; +} - /* 2c. Let directory voting happen. */ - if (authdir_mode_v3(options)) - dirvote_act(options, now); +static int +fetch_networkstatus_callback(time_t now, const or_options_t *options) +{ + /* 2c. Every minute (or every second if TestingTorNetwork, or during + * client bootstrap), check whether we want to download any networkstatus + * documents. */ - /* 3a. Every second, we examine pending circuits and prune the - * ones which have been pending for more than a few seconds. - * We do this before step 4, so it can try building more if - * it's not comfortable with the number of available circuits. - */ - /* (If our circuit build timeout can ever become lower than a second (which - * it can't, currently), we should do this more often.) */ - circuit_expire_building(); + /* How often do we check whether we should download network status + * documents? */ + const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( + now); + const int prefer_mirrors = !directory_fetches_from_authorities( + get_options()); + int networkstatus_dl_check_interval = 60; + /* check more often when testing, or when bootstrapping from mirrors + * (connection limits prevent too many connections being made) */ + if (options->TestingTorNetwork + || (we_are_bootstrapping && prefer_mirrors)) { + networkstatus_dl_check_interval = 1; + } - /* 3b. Also look at pending streams and prune the ones that 'began' - * a long time ago but haven't gotten a 'connected' yet. - * Do this before step 4, so we can put them back into pending - * state to be picked up by the new circuit. - */ - connection_ap_expire_beginning(); + if (should_delay_dir_fetches(options, NULL)) + return PERIODIC_EVENT_NO_UPDATE; - /* 3c. And expire connections that we've held open for too long. - */ - connection_expire_held_open(); + update_networkstatus_downloads(now); + return networkstatus_dl_check_interval; +} +static int +retry_listeners_callback(time_t now, const or_options_t *options) +{ + (void)now; + (void)options; /* 3d. And every 60 seconds, we relaunch listeners if any died. */ - if (!net_is_disabled() && time_to_check_listeners < now) { + if (!net_is_disabled()) { retry_all_listeners(NULL, NULL, 0); - time_to_check_listeners = now+60; - } - - /* 4. Every second, we try a new circuit if there are no valid - * circuits. Every NewCircuitPeriod seconds, we expire circuits - * that became dirty more than MaxCircuitDirtiness seconds ago, - * and we make a new circ if there are no clean circuits. - */ - have_dir_info = router_have_minimum_dir_info(); - if (have_dir_info && !net_is_disabled()) { - circuit_build_needed_circs(now); - } else { - circuit_expire_old_circs_as_needed(now); + return 60; } + return PERIODIC_EVENT_NO_UPDATE; +} - /* every 10 seconds, but not at the same second as other such events */ - if (now % 10 == 5) - circuit_expire_old_circuits_serverside(now); - - /* 5. We do housekeeping for each connection... */ - connection_or_set_bad_connections(NULL, 0); - for (i=0;i<smartlist_len(connection_array);i++) { - run_connection_housekeeping(i, now); - } - - /* 6. And remove any marked circuits... */ - circuit_close_all_marked(); - - /* 7. And upload service descriptors if necessary. */ - if (have_completed_a_circuit() && !net_is_disabled()) { - rend_consider_services_upload(now); - rend_consider_descriptor_republication(); - } - - /* 8. and blow away any connections that need to die. have to do this now, - * because if we marked a conn for close and left its socket -1, then - * we'll pass it to poll/select and bad things will happen. - */ - close_closeable_connections(); - - /* 8b. And if anything in our state is ready to get flushed to disk, we - * flush it. */ - or_state_save(now); - - /* 8c. Do channel cleanup just like for connections */ - channel_run_cleanup(); - channel_listener_run_cleanup(); +static int +expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options) +{ + (void)options; + /* every 11 seconds, so not usually the same second as other such events */ + circuit_expire_old_circuits_serverside(now); + return 11; +} +static int +check_dns_honesty_callback(time_t now, const or_options_t *options) +{ + (void)now; /* 9. and if we're an exit node, check whether our DNS is telling stories * to us. */ - if (!net_is_disabled() && - public_server_mode(options) && - time_to_check_for_correct_dns < now && - ! router_my_exit_policy_is_reject_star()) { - if (!time_to_check_for_correct_dns) { - time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); - } else { - dns_launch_correctness_checks(); - time_to_check_for_correct_dns = now + 12*3600 + - crypto_rand_int(12*3600); - } + if (net_is_disabled() || + ! public_server_mode(options) || + router_my_exit_policy_is_reject_star()) + return PERIODIC_EVENT_NO_UPDATE; + + static int first_time = 1; + if (first_time) { + /* Don't launch right when we start */ + first_time = 0; + return crypto_rand_int_range(60, 180); } + dns_launch_correctness_checks(); + return 12*3600 + crypto_rand_int(12*3600); +} + +static int +write_bridge_ns_callback(time_t now, const or_options_t *options) +{ /* 10. write bridge networkstatus file to disk */ - if (options->BridgeAuthoritativeDir && - time_to_write_bridge_status_file < now) { + if (options->BridgeAuthoritativeDir) { networkstatus_dump_bridge_status_to_file(now); #define BRIDGE_STATUSFILE_INTERVAL (30*60) - time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL; + return BRIDGE_STATUSFILE_INTERVAL; } + return PERIODIC_EVENT_NO_UPDATE; +} +static int +check_fw_helper_app_callback(time_t now, const or_options_t *options) +{ + if (net_is_disabled() || + ! server_mode(options) || + ! options->PortForwarding) { + return PERIODIC_EVENT_NO_UPDATE; + } /* 11. check the port forwarding app */ - if (!net_is_disabled() && - time_to_check_port_forwarding < now && - options->PortForwarding && - is_server) { + #define PORT_FORWARDING_CHECK_INTERVAL 5 - smartlist_t *ports_to_forward = get_list_of_ports_to_forward(); - if (ports_to_forward) { - tor_check_port_forwarding(options->PortForwardingHelper, - ports_to_forward, - now); - - SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp)); - smartlist_free(ports_to_forward); - } - time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL; + smartlist_t *ports_to_forward = get_list_of_ports_to_forward(); + if (ports_to_forward) { + tor_check_port_forwarding(options->PortForwardingHelper, + ports_to_forward, + now); + + SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp)); + smartlist_free(ports_to_forward); } + return PORT_FORWARDING_CHECK_INTERVAL; +} - /* 11b. check pending unconfigured managed proxies */ - if (!net_is_disabled() && pt_proxies_configuration_pending()) - pt_configure_remaining_proxies(); +/** Callback to write heartbeat message in the logs. */ +static int +heartbeat_callback(time_t now, const or_options_t *options) +{ + static int first = 1; + + /* Check if heartbeat is disabled */ + if (!options->HeartbeatPeriod) { + return PERIODIC_EVENT_NO_UPDATE; + } - /* 12. write the heartbeat message */ - if (options->HeartbeatPeriod && - time_to_next_heartbeat <= now) { - if (time_to_next_heartbeat) /* don't log the first heartbeat */ - log_heartbeat(now); - time_to_next_heartbeat = now+options->HeartbeatPeriod; + /* Write the heartbeat message */ + if (first) { + first = 0; /* Skip the first one. */ + } else { + log_heartbeat(now); } + + return options->HeartbeatPeriod; } /** Timer: used to invoke second_elapsed_callback() once per second. */ @@ -1715,11 +2089,12 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT) { /* every 20 minutes, check and complain if necessary */ const routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable()) { + if (me && !check_whether_orport_reachable(options)) { char *address = tor_dup_ip(me->addr); log_warn(LD_CONFIG,"Your server (%s:%d) has not managed to confirm that " - "its ORPort is reachable. Please check your firewalls, ports, " - "address, /etc/hosts file, etc.", + "its ORPort is reachable. Relays do not publish descriptors " + "until their ORPort and DirPort are reachable. Please check " + "your firewalls, ports, address, /etc/hosts file, etc.", address, me->or_port); control_event_server_status(LOG_WARN, "REACHABILITY_FAILED ORADDRESS=%s:%d", @@ -1727,12 +2102,13 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) tor_free(address); } - if (me && !check_whether_dirport_reachable()) { + if (me && !check_whether_dirport_reachable(options)) { char *address = tor_dup_ip(me->addr); log_warn(LD_CONFIG, "Your server (%s:%d) has not managed to confirm that its " - "DirPort is reachable. Please check your firewalls, ports, " - "address, /etc/hosts file, etc.", + "DirPort is reachable. Relays do not publish descriptors " + "until their ORPort and DirPort are reachable. Please check " + "your firewalls, ports, address, /etc/hosts file, etc.", address, me->dir_port); control_event_server_status(LOG_WARN, "REACHABILITY_FAILED DIRADDRESS=%s:%d", @@ -1747,8 +2123,6 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg) if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN || seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) { circuit_note_clock_jumped(seconds_elapsed); - /* XXX if the time jumps *back* many months, do our events in - * run_scheduled_events() recover? I don't think they do. -RD */ } else if (seconds_elapsed > 0) stats_n_seconds_working += seconds_elapsed; @@ -1844,12 +2218,15 @@ got_libevent_error(void) void ip_address_changed(int at_interface) { - int server = server_mode(get_options()); + const or_options_t *options = get_options(); + int server = server_mode(options); + int exit_reject_private = (server && options->ExitRelay + && options->ExitPolicyRejectPrivate); if (at_interface) { if (! server) { /* Okay, change our keys. */ - if (init_keys()<0) + if (init_keys_client() < 0) log_warn(LD_GENERAL, "Unable to rotate keys after IP change!"); } } else { @@ -1858,10 +2235,15 @@ ip_address_changed(int at_interface) reset_bandwidth_test(); stats_n_seconds_working = 0; router_reset_reachability(); - mark_my_descriptor_dirty("IP address changed"); } } + /* Exit relays incorporate interface addresses in their exit policies when + * ExitPolicyRejectPrivate is set */ + if (exit_reject_private || (server && !at_interface)) { + mark_my_descriptor_dirty("IP address changed"); + } + dns_servers_relaunch_checks(); } @@ -1872,7 +2254,10 @@ dns_servers_relaunch_checks(void) { if (server_mode(get_options())) { dns_reset_correctness_checks(); - time_to_check_for_correct_dns = 0; + if (periodic_events_initialized) { + tor_assert(check_dns_honesty_event); + periodic_event_reschedule(check_dns_honesty_event); + } } } @@ -1944,6 +2329,14 @@ do_hup(void) * force a retry there. */ if (server_mode(options)) { + /* Maybe we've been given a new ed25519 key or certificate? + */ + time_t now = approx_time(); + if (load_ed_keys(options, now) < 0 || + generate_ed_link_cert(options, now)) { + log_warn(LD_OR, "Problem reloading Ed25519 keys; still using old keys."); + } + /* Update cpuworker and dnsworker processes, so they get up-to-date * configuration options. */ cpuworkers_rotate_keyinfo(); @@ -1958,6 +2351,13 @@ do_main_loop(void) { time_t now; + /* initialize the periodic events first, so that code that depends on the + * events being present does not assert. + */ + if (! periodic_events_initialized) { + initialize_periodic_events(); + } + /* initialize dns resolve map, spawn workers if needed */ if (dns_init() < 0) { if (get_options()->ServerDNSAllowBrokenConfig) @@ -1982,7 +2382,7 @@ do_main_loop(void) * TLS context. */ if (! client_identity_key_is_set()) { if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; exiting"); + log_err(LD_OR, "Error initializing keys; exiting"); return -1; } } @@ -1997,6 +2397,34 @@ do_main_loop(void) /* initialize the bootstrap status events to know we're starting up */ control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0); + /* Initialize the keypinning log. */ + if (authdir_mode_v3(get_options())) { + char *fname = get_datadir_fname("key-pinning-journal"); + int r = 0; + if (keypin_load_journal(fname)<0) { + log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno)); + r = -1; + } + if (keypin_open_journal(fname)<0) { + log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno)); + r = -1; + } + tor_free(fname); + if (r) + return r; + } + { + /* This is the old name for key-pinning-journal. These got corrupted + * in a couple of cases by #16530, so we started over. See #16580 for + * the rationale and for other options we didn't take. We can remove + * this code once all the authorities that ran 0.2.7.1-alpha-dev are + * upgraded. + */ + char *fname = get_datadir_fname("key-pinning-entries"); + unlink(fname); + tor_free(fname); + } + if (trusted_dirs_reload_certs()) { log_warn(LD_DIR, "Couldn't load all cached v3 certificates. Starting anyway."); @@ -2012,7 +2440,7 @@ do_main_loop(void) * appropriate.) */ now = time(NULL); - directory_info_has_arrived(now, 1); + directory_info_has_arrived(now, 1, 0); if (server_mode(get_options())) { /* launch cpuworkers. Need to do this *after* we've read the onion key. */ @@ -2140,6 +2568,11 @@ run_main_loop_once(void) } } + /* This will be pretty fast if nothing new is pending. Note that this gets + * called once per libevent loop, which will make it happen once per group + * of events that fire, or once per second. */ + connection_ap_attach_pending(0); + return 1; } @@ -2158,23 +2591,22 @@ run_main_loop_until_done(void) return loop_result; } -#ifndef _WIN32 /* Only called when we're willing to use signals */ /** Libevent callback: invoked when we get a signal. */ static void -signal_callback(int fd, short events, void *arg) +signal_callback(evutil_socket_t fd, short events, void *arg) { - uintptr_t sig = (uintptr_t)arg; + const int *sigptr = arg; + const int sig = *sigptr; (void)fd; (void)events; process_signal(sig); } -#endif /** Do the work of acting on a signal received in <b>sig</b> */ -void -process_signal(uintptr_t sig) +static void +process_signal(int sig) { switch (sig) { @@ -2315,12 +2747,13 @@ dumpstats(int severity) if (conn->type == CONN_TYPE_OR) { or_connection_t *or_conn = TO_OR_CONN(conn); if (or_conn->tls) { - tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, - &wbuf_cap, &wbuf_len); - tor_log(severity, LD_GENERAL, - "Conn %d: %d/%d bytes used on OpenSSL read buffer; " - "%d/%d bytes used on write buffer.", - i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); + if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len, + &wbuf_cap, &wbuf_len) == 0) { + tor_log(severity, LD_GENERAL, + "Conn %d: %d/%d bytes used on OpenSSL read buffer; " + "%d/%d bytes used on write buffer.", + i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap); + } } } } @@ -2397,35 +2830,73 @@ exit_function(void) #endif } -/** Set up the signal handlers for either parent or child. */ +#ifdef _WIN32 +#define UNIX_ONLY 0 +#else +#define UNIX_ONLY 1 +#endif +static struct { + int signal_value; + int try_to_register; + struct event *signal_event; +} signal_handlers[] = { +#ifdef SIGINT + { SIGINT, UNIX_ONLY, NULL }, /* do a controlled slow shutdown */ +#endif +#ifdef SIGTERM + { SIGTERM, UNIX_ONLY, NULL }, /* to terminate now */ +#endif +#ifdef SIGPIPE + { SIGPIPE, UNIX_ONLY, NULL }, /* otherwise SIGPIPE kills us */ +#endif +#ifdef SIGUSR1 + { SIGUSR1, UNIX_ONLY, NULL }, /* dump stats */ +#endif +#ifdef SIGUSR2 + { SIGUSR2, UNIX_ONLY, NULL }, /* go to loglevel debug */ +#endif +#ifdef SIGHUP + { SIGHUP, UNIX_ONLY, NULL }, /* to reload config, retry conns, etc */ +#endif +#ifdef SIGXFSZ + { SIGXFSZ, UNIX_ONLY, NULL }, /* handle file-too-big resource exhaustion */ +#endif +#ifdef SIGCHLD + { SIGCHLD, UNIX_ONLY, NULL }, /* handle dns/cpu workers that exit */ +#endif + /* These are controller-only */ + { SIGNEWNYM, 0, NULL }, + { SIGCLEARDNSCACHE, 0, NULL }, + { SIGHEARTBEAT, 0, NULL }, + { -1, -1, NULL } +}; + +/** Set up the signal handlers for either parent or child process */ void handle_signals(int is_parent) { -#ifndef _WIN32 /* do signal stuff only on Unix */ int i; - static const int signals[] = { - SIGINT, /* do a controlled slow shutdown */ - SIGTERM, /* to terminate now */ - SIGPIPE, /* otherwise SIGPIPE kills us */ - SIGUSR1, /* dump stats */ - SIGUSR2, /* go to loglevel debug */ - SIGHUP, /* to reload config, retry conns, etc */ -#ifdef SIGXFSZ - SIGXFSZ, /* handle file-too-big resource exhaustion */ -#endif - SIGCHLD, /* handle dns/cpu workers that exit */ - -1 }; - static struct event *signal_events[16]; /* bigger than it has to be. */ if (is_parent) { - for (i = 0; signals[i] >= 0; ++i) { - signal_events[i] = tor_evsignal_new( - tor_libevent_get_base(), signals[i], signal_callback, - (void*)(uintptr_t)signals[i]); - if (event_add(signal_events[i], NULL)) - log_warn(LD_BUG, "Error from libevent when adding event for signal %d", - signals[i]); + for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { + if (signal_handlers[i].try_to_register) { + signal_handlers[i].signal_event = + tor_evsignal_new(tor_libevent_get_base(), + signal_handlers[i].signal_value, + signal_callback, + &signal_handlers[i].signal_value); + if (event_add(signal_handlers[i].signal_event, NULL)) + log_warn(LD_BUG, "Error from libevent when adding " + "event for signal %d", + signal_handlers[i].signal_value); + } else { + signal_handlers[i].signal_event = + tor_event_new(tor_libevent_get_base(), -1, + EV_SIGNAL, signal_callback, + &signal_handlers[i].signal_value); + } } } else { +#ifndef _WIN32 struct sigaction action; action.sa_flags = 0; sigemptyset(&action.sa_mask); @@ -2439,10 +2910,21 @@ handle_signals(int is_parent) #ifdef SIGXFSZ sigaction(SIGXFSZ, &action, NULL); #endif +#endif + } +} + +/* Make sure the signal handler for signal_num will be called. */ +void +activate_signal(int signal_num) +{ + int i; + for (i = 0; signal_handlers[i].signal_value >= 0; ++i) { + if (signal_handlers[i].signal_value == signal_num) { + event_active(signal_handlers[i].signal_event, EV_SIGNAL, 1); + return; + } } -#else /* MS windows */ - (void)is_parent; -#endif /* signal stuff */ } /** Main entry point for the Tor command-line client. @@ -2484,10 +2966,11 @@ tor_init(int argc, char *argv[]) if (!strcmp(cl->key, "--quiet") || !strcmp(cl->key, "--dump-config")) quiet = 2; - /* --version, --digests, and --help imply --hush */ + /* The following options imply --hush */ if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") || !strcmp(cl->key, "--list-torrc-options") || !strcmp(cl->key, "--library-versions") || + !strcmp(cl->key, "--hash-password") || !strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) { if (quiet < 1) quiet = 1; @@ -2664,8 +3147,8 @@ tor_free_all(int postfork) channel_tls_free_all(); channel_free_all(); connection_free_all(); + connection_edge_free_all(); scheduler_free_all(); - memarea_clear_freelist(); nodelist_free_all(); microdesc_free_all(); ext_orport_free_all(); @@ -2675,6 +3158,7 @@ tor_free_all(int postfork) config_free_all(); or_state_free_all(); router_free_all(); + routerkeys_free_all(); policies_free_all(); } if (!postfork) { @@ -2689,6 +3173,7 @@ tor_free_all(int postfork) smartlist_free(closeable_connection_lst); smartlist_free(active_linked_connection_lst); periodic_timer_free(second_timer); + teardown_periodic_events(); #ifndef USE_BUFFEREVENTS periodic_timer_free(refill_timer); #endif @@ -2732,6 +3217,7 @@ tor_cleanup(void) or_state_save(now); if (authdir_mode_tests_reachability(options)) rep_hist_record_mtbf_data(now, 0); + keypin_close_journal(); } #ifdef USE_DMALLOC dmalloc_log_stats(); @@ -2754,6 +3240,7 @@ do_list_fingerprint(void) char buf[FINGERPRINT_LEN+1]; crypto_pk_t *k; const char *nickname = get_options()->Nickname; + sandbox_disable_getaddrinfo_cache(); if (!server_mode(get_options())) { log_err(LD_GENERAL, "Clients don't have long-term identity keys. Exiting."); @@ -2761,7 +3248,7 @@ do_list_fingerprint(void) } tor_assert(nickname); if (init_keys() < 0) { - log_err(LD_BUG,"Error initializing keys; can't display fingerprint"); + log_err(LD_GENERAL,"Error initializing keys; exiting."); return -1; } if (!(k = get_server_identity_key())) { @@ -2803,6 +3290,7 @@ do_dump_config(void) const char *arg = options->command_arg; int how; char *opts; + if (!strcmp(arg, "short")) { how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "non-builtin")) { @@ -2810,8 +3298,9 @@ do_dump_config(void) } else if (!strcmp(arg, "full")) { how = OPTIONS_DUMP_ALL; } else { - printf("%s is not a recognized argument to --dump-config. " - "Please select 'short', 'non-builtin', or 'full'", arg); + fprintf(stderr, "No valid argument to --dump-config found!\n"); + fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n"); + return -1; } @@ -2825,6 +3314,13 @@ do_dump_config(void) static void init_addrinfo(void) { + if (! server_mode(get_options()) || + (get_options()->Address && strlen(get_options()->Address) > 0)) { + /* We don't need to seed our own hostname, because we won't be calling + * resolve_my_address on it. + */ + return; + } char hname[256]; // host name to sandbox @@ -2861,6 +3357,8 @@ sandbox_init_filter(void) OPEN_DATADIR2(name, name2 suffix); \ } while (0) + OPEN(options->DataDirectory); + OPEN_DATADIR("keys"); OPEN_DATADIR_SUFFIX("cached-certs", ".tmp"); OPEN_DATADIR_SUFFIX("cached-consensus", ".tmp"); OPEN_DATADIR_SUFFIX("unverified-consensus", ".tmp"); @@ -2877,12 +3375,19 @@ sandbox_init_filter(void) OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp"); + OPEN_DATADIR("key-pinning-journal"); OPEN("/dev/srandom"); OPEN("/dev/urandom"); OPEN("/dev/random"); OPEN("/etc/hosts"); OPEN("/proc/meminfo"); + if (options->BridgeAuthoritativeDir) + OPEN_DATADIR_SUFFIX("networkstatus-bridges", ".tmp"); + + if (authdir_mode_handles_descs(options, -1)) + OPEN_DATADIR("approved-routers"); + if (options->ServerDNSResolvConfFile) sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->ServerDNSResolvConfFile)); @@ -2923,6 +3428,9 @@ sandbox_init_filter(void) RENAME_SUFFIX("unparseable-desc", ".tmp"); RENAME_SUFFIX("v3-status-votes", ".tmp"); + if (options->BridgeAuthoritativeDir) + RENAME_SUFFIX("networkstatus-bridges", ".tmp"); + #define STAT_DATADIR(name) \ sandbox_cfg_allow_stat_filename(&cfg, get_datadir_fname(name)) @@ -2976,6 +3484,20 @@ sandbox_init_filter(void) } } + SMARTLIST_FOREACH_BEGIN(get_configured_ports(), port_cfg_t *, port) { + if (!port->is_unix_addr) + continue; + /* When we open an AF_UNIX address, we want permission to open the + * directory that holds it. */ + char *dirname = tor_strdup(port->unix_addr); + if (get_parent_directory(dirname) == 0) { + OPEN(dirname); + } + tor_free(dirname); + sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr)); + sandbox_cfg_allow_chown_filename(&cfg, tor_strdup(port->unix_addr)); + } SMARTLIST_FOREACH_END(port); + if (options->DirPortFrontPage) { sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->DirPortFrontPage)); @@ -2991,6 +3513,16 @@ sandbox_init_filter(void) OPEN_DATADIR2("keys", "secret_onion_key.old"); OPEN_DATADIR2("keys", "secret_onion_key_ntor.old"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_secret_key_encrypted", + ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_master_id_public_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_secret_key_encrypted", + ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_public_key", ".tmp"); + OPEN_DATADIR2_SUFFIX("keys", "ed25519_signing_cert", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "bridge-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "dirreq-stats", ".tmp"); @@ -2998,6 +3530,7 @@ sandbox_init_filter(void) OPEN_DATADIR2_SUFFIX("stats", "exit-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp"); OPEN_DATADIR("approved-routers"); OPEN_DATADIR_SUFFIX("fingerprint", ".tmp"); @@ -3022,6 +3555,12 @@ sandbox_init_filter(void) RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_secret_key_encrypted", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_master_id_public_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_signing_secret_key", ".tmp"); + RENAME_SUFFIX2("keys", "ed25519_signing_cert", ".tmp"); + sandbox_cfg_allow_rename(&cfg, get_datadir_fname2("keys", "secret_onion_key"), get_datadir_fname2("keys", "secret_onion_key.old")); @@ -3030,6 +3569,7 @@ sandbox_init_filter(void) get_datadir_fname2("keys", "secret_onion_key_ntor.old")); STAT_DATADIR("keys"); + OPEN_DATADIR("stats"); STAT_DATADIR("stats"); STAT_DATADIR2("stats", "dirreq-stats"); } @@ -3105,6 +3645,9 @@ tor_main(int argc, char *argv[]) #endif result = do_main_loop(); break; + case CMD_KEYGEN: + result = load_ed_keys(get_options(), time(NULL)); + break; case CMD_LIST_FINGERPRINT: result = do_list_fingerprint(); break; @@ -3113,7 +3656,8 @@ tor_main(int argc, char *argv[]) result = 0; break; case CMD_VERIFY_CONFIG: - printf("Configuration was valid\n"); + if (quiet_level == 0) + printf("Configuration was valid\n"); result = 0; break; case CMD_DUMP_CONFIG: diff --git a/src/or/main.h b/src/or/main.h index f77b4711c5..ad865b8124 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -48,18 +48,20 @@ MOCK_DECL(void,connection_start_writing,(connection_t *conn)); void connection_stop_reading_from_linked_conn(connection_t *conn); void directory_all_unreachable(time_t now); -void directory_info_has_arrived(time_t now, int from_cache); +void directory_info_has_arrived(time_t now, int from_cache, int suppress_logs); void ip_address_changed(int at_interface); void dns_servers_relaunch_checks(void); +void reset_all_main_loop_timers(void); void reschedule_descriptor_update_check(void); +void reschedule_directory_downloads(void); MOCK_DECL(long,get_uptime,(void)); unsigned get_signewnym_epoch(void); void handle_signals(int is_parent); -void process_signal(uintptr_t sig); +void activate_signal(int signal_num); int try_locking(const or_options_t *options, int err_if_locked); int have_lockfile(void); @@ -76,6 +78,8 @@ int tor_init(int argc, char **argv); #ifdef MAIN_PRIVATE STATIC void init_connection_lists(void); STATIC void close_closeable_connections(void); +STATIC void initialize_periodic_events(void); +STATIC void teardown_periodic_events(void); #endif #endif diff --git a/src/or/microdesc.c b/src/or/microdesc.c index 0511e870d1..5b5c29a6d2 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -1,6 +1,13 @@ -/* Copyright (c) 2009-2015, The Tor Project, Inc. */ +/* Copyright (c) 2009-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file microdesc.c + * + * \brief Implements microdescriptors -- an abbreviated description of + * less-frequently-changing router information. + */ + #include "or.h" #include "circuitbuild.h" #include "config.h" @@ -39,17 +46,23 @@ struct microdesc_cache_t { uint64_t total_len_seen; /** Total number of microdescriptors we have added to this cache */ unsigned n_seen; + + /** True iff we have loaded this cache from disk ever. */ + int is_loaded; }; +static microdesc_cache_t *get_microdesc_cache_noload(void); + /** Helper: computes a hash of <b>md</b> to place it in a hash table. */ -static INLINE unsigned int +static inline unsigned int microdesc_hash_(microdesc_t *md) { return (unsigned) siphash24g(md->digest, sizeof(md->digest)); } -/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */ -static INLINE int +/** Helper: compares <b>a</b> and <b>b</b> for equality for hash-table + * purposes. */ +static inline int microdesc_eq_(microdesc_t *a, microdesc_t *b) { return tor_memeq(a->digest, b->digest, DIGEST256_LEN); @@ -113,12 +126,23 @@ static microdesc_cache_t *the_microdesc_cache = NULL; microdesc_cache_t * get_microdesc_cache(void) { + microdesc_cache_t *cache = get_microdesc_cache_noload(); + if (PREDICT_UNLIKELY(cache->is_loaded == 0)) { + microdesc_cache_reload(cache); + } + return cache; +} + +/** Return a pointer to the microdescriptor cache, creating (but not loading) + * it if necessary. */ +static microdesc_cache_t * +get_microdesc_cache_noload(void) +{ if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) { - microdesc_cache_t *cache = tor_malloc_zero(sizeof(microdesc_cache_t)); + microdesc_cache_t *cache = tor_malloc_zero(sizeof(*cache)); HT_INIT(microdesc_map, &cache->map); cache->cache_fname = get_datadir_fname("cached-microdescs"); cache->journal_fname = get_datadir_fname("cached-microdescs.new"); - microdesc_cache_reload(cache); the_microdesc_cache = cache; } return the_microdesc_cache; @@ -353,6 +377,8 @@ microdesc_cache_reload(microdesc_cache_t *cache) microdesc_cache_clear(cache); + cache->is_loaded = 1; + mm = cache->cache_content = tor_mmap_file(cache->cache_fname); if (mm) { added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size, @@ -697,7 +723,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) /* Make sure that the microdesc was really removed from the appropriate data structures. */ if (md->held_in_map) { - microdesc_cache_t *cache = get_microdesc_cache(); + microdesc_cache_t *cache = get_microdesc_cache_noload(); microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md); if (md2 == md) { log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still " @@ -710,7 +736,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) tor_fragile_assert(); } if (md->held_by_nodes) { - microdesc_cache_t *cache = get_microdesc_cache(); + microdesc_cache_t *cache = get_microdesc_cache_noload(); int found=0; const smartlist_t *nodes = nodelist_get_list(); const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map); @@ -738,6 +764,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) if (md->onion_pkey) crypto_pk_free(md->onion_pkey); tor_free(md->onion_curve25519_pkey); + tor_free(md->ed25519_identity_pkey); if (md->body && md->saved_location != SAVED_IN_CACHE) tor_free(md->body); @@ -821,7 +848,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, /** Launch download requests for microdescriptors as appropriate. * * Specifically, we should launch download requests if we are configured to - * download mirodescriptors, and there are some microdescriptors listed the + * download mirodescriptors, and there are some microdescriptors listed in the * current microdesc consensus that we don't have, and either we never asked * for them, or we failed to download them but we're willing to retry. */ @@ -928,8 +955,8 @@ we_fetch_router_descriptors(const or_options_t *options) } /** Return the consensus flavor we actually want to use to build circuits. */ -int -usable_consensus_flavor(void) +MOCK_IMPL(int, +usable_consensus_flavor,(void)) { if (we_use_microdescriptors_for_circuits(get_options())) { return FLAV_MICRODESC; diff --git a/src/or/microdesc.h b/src/or/microdesc.h index 08571e4bd5..40c83139e9 100644 --- a/src/or/microdesc.h +++ b/src/or/microdesc.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -47,7 +47,7 @@ void microdesc_free_all(void); void update_microdesc_downloads(time_t now); void update_microdescs_from_networkstatus(time_t now); -int usable_consensus_flavor(void); +MOCK_DECL(int, usable_consensus_flavor,(void)); int we_fetch_microdescriptors(const or_options_t *options); int we_fetch_router_descriptors(const or_options_t *options); int we_use_microdescriptors_for_circuits(const or_options_t *options); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index da110fdff6..1cedfef9b7 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -85,8 +85,30 @@ static time_t time_to_download_next_consensus[N_CONSENSUS_FLAVORS]; /** Download status for the current consensus networkstatus. */ static download_status_t consensus_dl_status[N_CONSENSUS_FLAVORS] = { - { 0, 0, DL_SCHED_CONSENSUS }, - { 0, 0, DL_SCHED_CONSENSUS }, + { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE }, + { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_FAILURE }, + }; + +#define N_CONSENSUS_BOOTSTRAP_SCHEDULES 2 +#define CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY 0 +#define CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER 1 + +/* Using DL_SCHED_INCREMENT_ATTEMPT on these schedules means that + * download_status_increment_failure won't increment these entries. + * However, any bootstrap connection failures that occur after we have + * a valid consensus will count against the failure counts on the non-bootstrap + * schedules. There should only be one of these, as all the others will have + * been cancelled. (This doesn't seem to be a significant issue.) */ +static download_status_t + consensus_bootstrap_dl_status[N_CONSENSUS_BOOTSTRAP_SCHEDULES] = + { + { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_AUTHORITY, + DL_SCHED_INCREMENT_ATTEMPT }, + /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */ + { 0, 0, 0, DL_SCHED_CONSENSUS, DL_WANT_ANY_DIRSERVER, + DL_SCHED_INCREMENT_ATTEMPT }, }; /** True iff we have logged a warning about this OR's version being older than @@ -97,6 +119,9 @@ static int have_warned_about_old_version = 0; static int have_warned_about_new_version = 0; static void routerstatus_list_update_named_server_map(void); +static void update_consensus_bootstrap_multiple_downloads( + time_t now, + const or_options_t *options); /** Forget that we've warned about anything networkstatus-related, so we will * give fresh warnings if the same behavior happens again. */ @@ -122,6 +147,9 @@ networkstatus_reset_download_failures(void) for (i=0; i < N_CONSENSUS_FLAVORS; ++i) download_status_reset(&consensus_dl_status[i]); + + for (i=0; i < N_CONSENSUS_BOOTSTRAP_SCHEDULES; ++i) + download_status_reset(&consensus_bootstrap_dl_status[i]); } /** Read every cached v3 consensus networkstatus from the disk. */ @@ -734,6 +762,35 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor) * fetching certs before we check whether there is a better one? */ #define DELAY_WHILE_FETCHING_CERTS (20*60) +/* Check if a downloaded consensus flavor should still wait for certificates + * to download now. + * If so, return 1. If not, fail dls and return 0. */ +static int +check_consensus_waiting_for_certs(int flavor, time_t now, + download_status_t *dls) +{ + consensus_waiting_for_certs_t *waiting; + + /* We should always have a known flavor, because we_want_to_fetch_flavor() + * filters out unknown flavors. */ + tor_assert(flavor >= 0 && flavor < N_CONSENSUS_FLAVORS); + + waiting = &consensus_waiting_for_certs[flavor]; + if (waiting->consensus) { + /* XXXX make sure this doesn't delay sane downloads. */ + if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) { + return 1; + } else { + if (!waiting->dl_failed) { + download_status_failed(dls, 0); + waiting->dl_failed=1; + } + } + } + + return 0; +} + /** If we want to download a fresh consensus, launch a new download as * appropriate. */ static void @@ -741,12 +798,19 @@ update_consensus_networkstatus_downloads(time_t now) { int i; const or_options_t *options = get_options(); + const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( + now); + const int use_multi_conn = + networkstatus_consensus_can_use_multiple_directories(options); + + if (should_delay_dir_fetches(options, NULL)) + return; for (i=0; i < N_CONSENSUS_FLAVORS; ++i) { /* XXXX need some way to download unknown flavors if we are caching. */ const char *resource; - consensus_waiting_for_certs_t *waiting; networkstatus_t *c; + int max_in_progress_conns = 1; if (! we_want_to_fetch_flavor(options, i)) continue; @@ -762,35 +826,147 @@ update_consensus_networkstatus_downloads(time_t now) resource = networkstatus_get_flavor_name(i); - /* Let's make sure we remembered to update consensus_dl_status */ - tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS); + /* Check if we already have enough connections in progress */ + if (we_are_bootstrapping) { + max_in_progress_conns = + options->ClientBootstrapConsensusMaxInProgressTries; + } + if (connection_dir_count_by_purpose_and_resource( + DIR_PURPOSE_FETCH_CONSENSUS, + resource) + >= max_in_progress_conns) { + continue; + } - if (!download_status_is_ready(&consensus_dl_status[i], now, - options->TestingConsensusMaxDownloadTries)) - continue; /* We failed downloading a consensus too recently. */ - if (connection_dir_get_by_purpose_and_resource( - DIR_PURPOSE_FETCH_CONSENSUS, resource)) - continue; /* There's an in-progress download.*/ + /* Check if we want to launch another download for a usable consensus. + * Only used during bootstrap. */ + if (we_are_bootstrapping && use_multi_conn + && i == usable_consensus_flavor()) { - waiting = &consensus_waiting_for_certs[i]; - if (waiting->consensus) { - /* XXXX make sure this doesn't delay sane downloads. */ - if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) { - continue; /* We're still getting certs for this one. */ - } else { - if (!waiting->dl_failed) { - download_status_failed(&consensus_dl_status[i], 0); - waiting->dl_failed=1; - } + /* Check if we're already downloading a usable consensus */ + if (networkstatus_consensus_is_already_downloading(resource)) + continue; + + /* Make multiple connections for a bootstrap consensus download. */ + update_consensus_bootstrap_multiple_downloads(now, options); + } else { + /* Check if we failed downloading a consensus too recently */ + int max_dl_tries = options->TestingConsensusMaxDownloadTries; + + /* Let's make sure we remembered to update consensus_dl_status */ + tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS); + + if (!download_status_is_ready(&consensus_dl_status[i], + now, + max_dl_tries)) { + continue; } + + /* Check if we're waiting for certificates to download */ + if (check_consensus_waiting_for_certs(i, now, &consensus_dl_status[i])) + continue; + + /* Try the requested attempt */ + log_info(LD_DIR, "Launching %s standard networkstatus consensus " + "download.", networkstatus_get_flavor_name(i)); + directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS, + ROUTER_PURPOSE_GENERAL, resource, + PDS_RETRY_IF_NO_SERVERS, + consensus_dl_status[i].want_authority); } + } +} + +/** When we're bootstrapping, launch one or more consensus download + * connections, if schedule indicates connection(s) should be made after now. + * If is_authority, connect to an authority, otherwise, use a fallback + * directory mirror. + */ +static void +update_consensus_bootstrap_attempt_downloads( + time_t now, + const or_options_t *options, + download_status_t *dls, + download_want_authority_t want_authority) +{ + int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options); + int max_dl_tries = options->ClientBootstrapConsensusMaxDownloadTries; + if (!use_fallbacks) { + max_dl_tries = + options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries; + } - log_info(LD_DIR, "Launching %s networkstatus consensus download.", - networkstatus_get_flavor_name(i)); + const char *resource = networkstatus_get_flavor_name( + usable_consensus_flavor()); + + /* Let's make sure we remembered to update schedule */ + tor_assert(dls->schedule == DL_SCHED_CONSENSUS); + + /* Allow for multiple connections in the same second, if the schedule value + * is 0. */ + while (download_status_is_ready(dls, now, max_dl_tries)) { + log_info(LD_DIR, "Launching %s bootstrap %s networkstatus consensus " + "download.", resource, (want_authority == DL_WANT_AUTHORITY + ? "authority" + : "mirror")); directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS, ROUTER_PURPOSE_GENERAL, resource, - PDS_RETRY_IF_NO_SERVERS); + PDS_RETRY_IF_NO_SERVERS, want_authority); + /* schedule the next attempt */ + download_status_increment_attempt(dls, resource, now); + } +} + +/** If we're bootstrapping, check the connection schedules and see if we want + * to make additional, potentially concurrent, consensus download + * connections. + * Only call when bootstrapping, and when we want to make additional + * connections. Only nodes that satisfy + * networkstatus_consensus_can_use_multiple_directories make additional + * connections. + */ +static void +update_consensus_bootstrap_multiple_downloads(time_t now, + const or_options_t *options) +{ + const int usable_flavor = usable_consensus_flavor(); + + /* make sure we can use multiple connections */ + if (!networkstatus_consensus_can_use_multiple_directories(options)) { + return; + } + + /* Launch concurrent consensus download attempt(s) based on the mirror and + * authority schedules. Try the mirror first - this makes it slightly more + * likely that we'll connect to the fallback first, and then end the + * authority connection attempt. */ + + /* If a consensus download fails because it's waiting for certificates, + * we'll fail both the authority and fallback schedules. This is better than + * failing only one of the schedules, and having the other continue + * unchecked. + */ + + /* If we don't have or can't use extra fallbacks, don't try them. */ + if (networkstatus_consensus_can_use_extra_fallbacks(options)) { + download_status_t *dls_f = + &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_ANY_DIRSERVER]; + + if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) { + /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */ + update_consensus_bootstrap_attempt_downloads(now, options, dls_f, + DL_WANT_ANY_DIRSERVER); + } + } + + /* Now try an authority. */ + download_status_t *dls_a = + &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY]; + + if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) { + update_consensus_bootstrap_attempt_downloads(now, options, dls_a, + DL_WANT_AUTHORITY); } } @@ -856,8 +1032,8 @@ update_consensus_networkstatus_fetch_time_impl(time_t now, int flav) dl_interval = interval/2; } } else { - /* We're an ordinary client or a bridge. Give all the caches enough - * time to download the consensus. */ + /* We're an ordinary client, a bridge, or a hidden service. + * Give all the caches enough time to download the consensus. */ start = (time_t)(c->fresh_until + (interval*3)/4); /* But download the next one well before this one is expired. */ dl_interval = ((c->valid_until - start) * 7 )/ 8; @@ -1057,6 +1233,100 @@ networkstatus_get_reasonably_live_consensus(time_t now, int flavor) return NULL; } +/** Check if we need to download a consensus during tor's bootstrap phase. + * If we have no consensus, or our consensus is unusably old, return 1. + * As soon as we have received a consensus, return 0, even if we don't have + * enough certificates to validate it. + * If a fallback directory gives us a consensus we can never get certs for, + * check_consensus_waiting_for_certs() will wait 20 minutes before failing + * the cert downloads. After that, a new consensus will be fetched from a + * randomly chosen fallback. */ +MOCK_IMPL(int, +networkstatus_consensus_is_bootstrapping,(time_t now)) +{ + /* If we have a validated, reasonably live consensus, we're not + * bootstrapping a consensus at all. */ + if (networkstatus_get_reasonably_live_consensus( + now, + usable_consensus_flavor())) { + return 0; + } + + /* If we have a consensus, but we're waiting for certificates, + * we're not waiting for a consensus download while bootstrapping. */ + if (consensus_is_waiting_for_certs()) { + return 0; + } + + /* If we have no consensus, or our consensus is very old, we are + * bootstrapping, and we need to download a consensus. */ + return 1; +} + +/** Check if we can use multiple directories for a consensus download. + * Only clients (including bridge relays, which act like clients) benefit + * from multiple simultaneous consensus downloads. */ +int +networkstatus_consensus_can_use_multiple_directories( + const or_options_t *options) +{ + /* If we are a client, bridge, bridge client, or hidden service */ + return !public_server_mode(options); +} + +/** Check if we can use fallback directory mirrors for a consensus download. + * If we have fallbacks and don't want to fetch from the authorities, + * we can use them. */ +MOCK_IMPL(int, +networkstatus_consensus_can_use_extra_fallbacks,(const or_options_t *options)) +{ + /* The list length comparisons are a quick way to check if we have any + * non-authority fallback directories. If we ever have any authorities that + * aren't fallback directories, we will need to change this code. */ + tor_assert(smartlist_len(router_get_fallback_dir_servers()) + >= smartlist_len(router_get_trusted_dir_servers())); + /* If we don't fetch from the authorities, and we have additional mirrors, + * we can use them. */ + return (!directory_fetches_from_authorities(options) + && (smartlist_len(router_get_fallback_dir_servers()) + > smartlist_len(router_get_trusted_dir_servers()))); +} + +/* Is there a consensus fetch for flavor <b>resource</b> that's far + * enough along to be attached to a circuit? */ +int +networkstatus_consensus_is_already_downloading(const char *resource) +{ + int answer = 0; + + /* First, get a list of all the dir conns that are fetching a consensus, + * fetching *this* consensus, and are in state "reading" (meaning they + * have already flushed their request onto the socks connection). */ + smartlist_t *fetching_conns = + connection_dir_list_by_purpose_resource_and_state( + DIR_PURPOSE_FETCH_CONSENSUS, resource, DIR_CONN_STATE_CLIENT_READING); + + /* Then, walk through each conn, to see if its linked socks connection + * is in an attached state. We have to check this separately, since with + * the optimistic data feature, fetches can send their request to the + * socks connection and go into state 'reading', even before they're + * attached to any circuit. */ + SMARTLIST_FOREACH_BEGIN(fetching_conns, dir_connection_t *, dirconn) { + /* Do any of these other dir conns have a linked socks conn that is + * attached to a circuit already? */ + connection_t *base = TO_CONN(dirconn); + if (base->linked_conn && + base->linked_conn->type == CONN_TYPE_AP && + !AP_CONN_STATE_IS_UNATTACHED(base->linked_conn->state)) { + answer = 1; + break; /* stop looping, because we know the answer will be yes */ + } + } SMARTLIST_FOREACH_END(dirconn); + smartlist_free(fetching_conns); + + return answer; +} + /** Given two router status entries for the same router identity, return 1 if * if the contents have changed between them. Otherwise, return 0. */ static int @@ -1147,6 +1417,38 @@ networkstatus_copy_old_consensus_info(networkstatus_t *new_c, } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new); } +#ifdef TOR_UNIT_TESTS +/**Accept a <b>flavor</b> consensus <b>c</b> without any additional + * validation. This is exclusively for unit tests. + * We copy any ancillary information from a pre-existing consensus + * and then free the current one and replace it with the newly + * provided instance. Returns -1 on unrecognized flavor, 0 otherwise. + */ +int +networkstatus_set_current_consensus_from_ns(networkstatus_t *c, + const char *flavor) +{ + int flav = networkstatus_parse_flavor_name(flavor); + switch (flav) { + case FLAV_NS: + if (current_ns_consensus) { + networkstatus_copy_old_consensus_info(c, current_ns_consensus); + networkstatus_vote_free(current_ns_consensus); + } + current_ns_consensus = c; + break; + case FLAV_MICRODESC: + if (current_md_consensus) { + networkstatus_copy_old_consensus_info(c, current_md_consensus); + networkstatus_vote_free(current_md_consensus); + } + current_md_consensus = c; + break; + } + return current_md_consensus ? 0 : -1; +} +#endif //TOR_UNIT_TESTS + /** Try to replace the current cached v3 networkstatus with the one in * <b>consensus</b>. If we don't have enough certificates to validate it, * store it in consensus_waiting_for_certs and launch a certificate fetch. @@ -1180,7 +1482,7 @@ networkstatus_set_current_consensus(const char *consensus, const unsigned dl_certs = !(flags & NSSET_DONT_DOWNLOAD_CERTS); const unsigned accept_obsolete = flags & NSSET_ACCEPT_OBSOLETE; const unsigned require_flavor = flags & NSSET_REQUIRE_FLAVOR; - const digests_t *current_digests = NULL; + const common_digests_t *current_digests = NULL; consensus_waiting_for_certs_t *waiting = NULL; time_t current_valid_after = 0; int free_consensus = 1; /* Free 'c' at the end of the function */ @@ -1329,7 +1631,9 @@ networkstatus_set_current_consensus(const char *consensus, if (r != 1 && dl_certs) authority_certs_fetch_missing(c, now); - if (flav == usable_consensus_flavor()) { + const int is_usable_flavor = flav == usable_consensus_flavor(); + + if (is_usable_flavor) { notify_control_networkstatus_changed(current_consensus, c); } if (flav == FLAV_NS) { @@ -1372,20 +1676,12 @@ networkstatus_set_current_consensus(const char *consensus, } } - /* Reset the failure count only if this consensus is actually valid. */ - if (c->valid_after <= now && now <= c->valid_until) { - download_status_reset(&consensus_dl_status[flav]); - } else { - if (!from_cache) - download_status_failed(&consensus_dl_status[flav], 0); - } + if (is_usable_flavor) { + nodelist_set_consensus(c); - if (flav == usable_consensus_flavor()) { /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/ update_consensus_networkstatus_fetch_time(now); - nodelist_set_consensus(current_consensus); - dirvote_recalculate_timing(options, now); routerstatus_list_update_named_server_map(); @@ -1409,6 +1705,14 @@ networkstatus_set_current_consensus(const char *consensus, current_consensus); } + /* Reset the failure count only if this consensus is actually valid. */ + if (c->valid_after <= now && now <= c->valid_until) { + download_status_reset(&consensus_dl_status[flav]); + } else { + if (!from_cache) + download_status_failed(&consensus_dl_status[flav], 0); + } + if (directory_caches_dir_info(options)) { dirserv_set_cached_consensus_networkstatus(consensus, flavor, @@ -1678,7 +1982,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) dirserv_set_router_is_running(ri, now); /* then generate and write out status lines for each of them */ - set_routerstatus_from_routerinfo(&rs, node, ri, now, 0, 0); + set_routerstatus_from_routerinfo(&rs, node, ri, now, 0); smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs)); } SMARTLIST_FOREACH_END(ri); @@ -1697,11 +2001,10 @@ networkstatus_dump_bridge_status_to_file(time_t now) char *fname = NULL; char *thresholds = NULL; char *published_thresholds_and_status = NULL; - routerlist_t *rl = router_get_routerlist(); char published[ISO_TIME_LEN+1]; format_iso_time(published, now); - dirserv_compute_bridge_flag_thresholds(rl); + dirserv_compute_bridge_flag_thresholds(); thresholds = dirserv_get_flag_thresholds_line(); tor_asprintf(&published_thresholds_and_status, "published %s\nflag-thresholds %s\n%s", diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index d6e9e37013..ac93e5de91 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -70,6 +70,13 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor, networkstatus_t *networkstatus_get_live_consensus(time_t now); networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now, int flavor); +MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now)); +int networkstatus_consensus_can_use_multiple_directories( + const or_options_t *options); +MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,( + const or_options_t *options)); +int networkstatus_consensus_is_already_downloading(const char *resource); + #define NSSET_FROM_CACHE 1 #define NSSET_WAS_WAITING_FOR_CERTS 2 #define NSSET_DONT_DOWNLOAD_CERTS 4 @@ -106,6 +113,10 @@ int networkstatus_get_weight_scale_param(networkstatus_t *ns); #ifdef NETWORKSTATUS_PRIVATE STATIC void vote_routerstatus_free(vote_routerstatus_t *rs); +#ifdef TOR_UNIT_TESTS +STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c, + const char *flavor); +#endif // TOR_UNIT_TESTS #endif #endif diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 8f8adb42b5..89b5355c8d 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1,9 +1,17 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file nodelist.c + * + * \brief Structures and functions for tracking what we know about the routers + * on the Tor network, and correlating information from networkstatus, + * routerinfo, and microdescs. + */ + #include "or.h" #include "address.h" #include "config.h" @@ -57,13 +65,13 @@ typedef struct nodelist_t { } nodelist_t; -static INLINE unsigned int +static inline unsigned int node_id_hash(const node_t *node) { return (unsigned) siphash24g(node->identity, DIGEST_LEN); } -static INLINE unsigned int +static inline unsigned int node_id_eq(const node_t *node1, const node_t *node2) { return tor_memeq(node1->identity, node2->identity, DIGEST_LEN); @@ -176,7 +184,7 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out) if (authdir_mode(get_options()) && !had_router) { const char *discard=NULL; - uint32_t status = dirserv_router_get_status(ri, &discard); + uint32_t status = dirserv_router_get_status(ri, &discard, LOG_INFO); dirserv_set_node_flags_from_authoritative_status(node, status); } @@ -224,7 +232,6 @@ nodelist_set_consensus(networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); - int client = !server_mode(options); init_nodelist(); if (ns->flavor == FLAV_MICRODESC) @@ -261,7 +268,7 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_bad_exit = rs->is_bad_exit; node->is_hs_dir = rs->is_hs_dir; node->ipv6_preferred = 0; - if (client && options->ClientPreferIPv6ORPort == 1 && + if (fascist_firewall_prefer_ipv6_orport(options) && (tor_addr_is_null(&rs->ipv6_addr) == 0 || (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) node->ipv6_preferred = 1; @@ -291,7 +298,7 @@ nodelist_set_consensus(networkstatus_t *ns) } /** Helper: return true iff a node has a usable amount of information*/ -static INLINE int +static inline int node_is_usable(const node_t *node) { return (node->rs) || (node->ri); @@ -587,10 +594,10 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed)) "but none is listed as Named in the directory consensus. " "Choosing one arbitrarily.", nickname); } - } else if (smartlist_len(matches)>1 && warn_if_unnamed) { + } else if (smartlist_len(matches)==1 && warn_if_unnamed) { char fp[HEX_DIGEST_LEN+1]; node_t *node = smartlist_get(matches, 0); - if (node->name_lookup_warned) { + if (! node->name_lookup_warned) { base16_encode(fp, sizeof(fp), node->identity, DIGEST_LEN); log_warn(LD_CONFIG, "You specified a server \"%s\" by name, but the directory " @@ -644,12 +651,19 @@ node_is_named(const node_t *node) int node_is_dir(const node_t *node) { - if (node->rs) - return node->rs->dir_port != 0; - else if (node->ri) - return node->ri->dir_port != 0; - else + if (node->rs) { + routerstatus_t * rs = node->rs; + /* This is true if supports_tunnelled_dir_requests is true which + * indicates that we support directory request tunnelled or through the + * DirPort. */ + return rs->is_v2_dir; + } else if (node->ri) { + routerinfo_t * ri = node->ri; + /* Both tunnelled request is supported or DirPort is set. */ + return ri->supports_tunnelled_dir_requests; + } else { return 0; + } } /** Return true iff <b>node</b> has either kind of usable descriptor -- that @@ -754,6 +768,40 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family) return 1; } +/* Check if the "addr" and port_field fields from r are a valid non-listening + * address/port. If so, set valid to true and add a newly allocated + * tor_addr_port_t containing "addr" and port_field to sl. + * "addr" is an IPv4 host-order address and port_field is a uint16_t. + * r is typically a routerinfo_t or routerstatus_t. + */ +#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \ + STMT_BEGIN \ + if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ + valid = 1; \ + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ + tor_addr_from_ipv4h(&ap->addr, (r)->addr); \ + ap->port = (r)->port_field; \ + smartlist_add((sl), ap); \ + } \ + STMT_END + +/* Check if the "addr" and port_field fields from r are a valid non-listening + * address/port. If so, set valid to true and add a newly allocated + * tor_addr_port_t containing "addr" and port_field to sl. + * "addr" is a tor_addr_t and port_field is a uint16_t. + * r is typically a routerinfo_t or routerstatus_t. + */ +#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \ + STMT_BEGIN \ + if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \ + valid = 1; \ + tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ + tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \ + ap->port = (r)->port_field; \ + smartlist_add((sl), ap); \ + } \ + STMT_END + /** Return list of tor_addr_port_t with all OR ports (in the sense IP * addr + TCP port) for <b>node</b>. Caller must free all elements * using tor_free() and free the list using smartlist_free(). @@ -766,30 +814,38 @@ smartlist_t * node_get_all_orports(const node_t *node) { smartlist_t *sl = smartlist_new(); + int valid = 0; + /* Find a valid IPv4 address and port */ if (node->ri != NULL) { - if (node->ri->addr != 0) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_from_ipv4h(&ap->addr, node->ri->addr); - ap->port = node->ri->or_port; - smartlist_add(sl, ap); - } - if (!tor_addr_is_null(&node->ri->ipv6_addr)) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_copy(&ap->addr, &node->ri->ipv6_addr); - ap->port = node->ri->or_port; - smartlist_add(sl, ap); - } - } else if (node->rs != NULL) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_from_ipv4h(&ap->addr, node->rs->addr); - ap->port = node->rs->or_port; - smartlist_add(sl, ap); + SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid); + } + + /* If we didn't find a valid address/port in the ri, try the rs */ + if (!valid && node->rs != NULL) { + SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid); + } + + /* Find a valid IPv6 address and port */ + valid = 0; + if (node->ri != NULL) { + SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid); + } + + if (!valid && node->rs != NULL) { + SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid); + } + + if (!valid && node->md != NULL) { + SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid); } return sl; } +#undef SL_ADD_NEW_IPV4_AP +#undef SL_ADD_NEW_IPV6_AP + /** Wrapper around node_get_prim_orport for backward compatibility. */ void @@ -805,9 +861,13 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out) uint32_t node_get_prim_addr_ipv4h(const node_t *node) { - if (node->ri) { + /* Don't check the ORPort or DirPort, as this function isn't port-specific, + * and the node might have a valid IPv4 address, yet have a zero + * ORPort or DirPort. + */ + if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) { return node->ri->addr; - } else if (node->rs) { + } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) { return node->rs->addr; } return 0; @@ -818,13 +878,13 @@ node_get_prim_addr_ipv4h(const node_t *node) void node_get_address_string(const node_t *node, char *buf, size_t len) { - if (node->ri) { - strlcpy(buf, fmt_addr32(node->ri->addr), len); - } else if (node->rs) { + uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node); + + if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) { tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); + tor_addr_from_ipv4h(&addr, ipv4_addr); tor_addr_to_str(buf, &addr, len, 0); - } else { + } else if (len > 0) { buf[0] = '\0'; } } @@ -883,30 +943,83 @@ node_get_declared_family(const node_t *node) return NULL; } +/* Does this node have a valid IPv6 address? + * Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for + * checking specific ports. */ +int +node_has_ipv6_addr(const node_t *node) +{ + /* Don't check the ORPort or DirPort, as this function isn't port-specific, + * and the node might have a valid IPv6 address, yet have a zero + * ORPort or DirPort. + */ + if (node->ri && tor_addr_is_valid(&node->ri->ipv6_addr, 0)) + return 1; + if (node->rs && tor_addr_is_valid(&node->rs->ipv6_addr, 0)) + return 1; + if (node->md && tor_addr_is_valid(&node->md->ipv6_addr, 0)) + return 1; + + return 0; +} + +/* Does this node have a valid IPv6 ORPort? */ +int +node_has_ipv6_orport(const node_t *node) +{ + tor_addr_port_t ipv6_orport; + node_get_pref_ipv6_orport(node, &ipv6_orport); + return tor_addr_port_is_valid_ap(&ipv6_orport, 0); +} + +/* Does this node have a valid IPv6 DirPort? */ +int +node_has_ipv6_dirport(const node_t *node) +{ + tor_addr_port_t ipv6_dirport; + node_get_pref_ipv6_dirport(node, &ipv6_dirport); + return tor_addr_port_is_valid_ap(&ipv6_dirport, 0); +} + /** Return 1 if we prefer the IPv6 address and OR TCP port of * <b>node</b>, else 0. * - * We prefer the IPv6 address if the router has an IPv6 address and + * We prefer the IPv6 address if the router has an IPv6 address, + * and we can use IPv6 addresses, and: * i) the node_t says that it prefers IPv6 * or - * ii) the router has no IPv4 address. */ + * ii) the router has no IPv4 OR address. + * + * If you don't have a node, consider looking it up. + * If there is no node, use fascist_firewall_prefer_ipv6_orport(). + */ int -node_ipv6_preferred(const node_t *node) +node_ipv6_or_preferred(const node_t *node) { + const or_options_t *options = get_options(); tor_addr_port_t ipv4_addr; node_assert_ok(node); - if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) { - if (node->ri) - return !tor_addr_is_null(&node->ri->ipv6_addr); - if (node->md) - return !tor_addr_is_null(&node->md->ipv6_addr); - if (node->rs) - return !tor_addr_is_null(&node->rs->ipv6_addr); + /* XX/teor - node->ipv6_preferred is set from + * fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded. + */ + if (!fascist_firewall_use_ipv6(options)) { + return 0; + } else if (node->ipv6_preferred || node_get_prim_orport(node, &ipv4_addr)) { + return node_has_ipv6_orport(node); } return 0; } +#define RETURN_IPV4_AP(r, port_field, ap_out) \ + STMT_BEGIN \ + if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ + tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \ + (ap_out)->port = (r)->port_field; \ + return 0; \ + } \ + STMT_END + /** Copy the primary (IPv4) OR port (IP address and TCP port) for * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and * port was copied, else return non-zero.*/ @@ -916,20 +1029,10 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) node_assert_ok(node); tor_assert(ap_out); - if (node->ri) { - if (node->ri->addr == 0 || node->ri->or_port == 0) - return -1; - tor_addr_from_ipv4h(&ap_out->addr, node->ri->addr); - ap_out->port = node->ri->or_port; - return 0; - } - if (node->rs) { - if (node->rs->addr == 0 || node->rs->or_port == 0) - return -1; - tor_addr_from_ipv4h(&ap_out->addr, node->rs->addr); - ap_out->port = node->rs->or_port; - return 0; - } + RETURN_IPV4_AP(node->ri, or_port, ap_out); + RETURN_IPV4_AP(node->rs, or_port, ap_out); + /* Microdescriptors only have an IPv6 address */ + return -1; } @@ -938,21 +1041,12 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out) { - const or_options_t *options = get_options(); tor_assert(ap_out); - /* Cheap implementation of config option ClientUseIPv6 -- simply - don't prefer IPv6 when ClientUseIPv6 is not set and we're not a - client running with bridges. See #4455 for more on this subject. - - Note that this filter is too strict since we're hindering not - only clients! Erring on the safe side shouldn't be a problem - though. XXX move this check to where outgoing connections are - made? -LN */ - if ((options->ClientUseIPv6 || options->UseBridges) && - node_ipv6_preferred(node)) { + if (node_ipv6_or_preferred(node)) { node_get_pref_ipv6_orport(node, ap_out); } else { + /* the primary ORPort is always on IPv4 */ node_get_prim_orport(node, ap_out); } } @@ -965,20 +1059,115 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) node_assert_ok(node); tor_assert(ap_out); - /* We prefer the microdesc over a potential routerstatus here. They - are not being synchronised atm so there might be a chance that - they differ at some point, f.ex. when flipping - UseMicrodescriptors? -LN */ + /* Prefer routerstatus over microdesc for consistency with the + * fascist_firewall_* functions. Also check if the address or port are valid, + * and try another alternative if they are not. */ - if (node->ri) { + if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr, + node->ri->ipv6_orport, 0)) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); ap_out->port = node->ri->ipv6_orport; - } else if (node->md) { + } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr, + node->rs->ipv6_orport, 0)) { + tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); + ap_out->port = node->rs->ipv6_orport; + } else if (node->md && tor_addr_port_is_valid(&node->md->ipv6_addr, + node->md->ipv6_orport, 0)) { tor_addr_copy(&ap_out->addr, &node->md->ipv6_addr); ap_out->port = node->md->ipv6_orport; - } else if (node->rs) { + } else { + tor_addr_make_null(&ap_out->addr, AF_INET6); + ap_out->port = 0; + } +} + +/** Return 1 if we prefer the IPv6 address and Dir TCP port of + * <b>node</b>, else 0. + * + * We prefer the IPv6 address if the router has an IPv6 address, + * and we can use IPv6 addresses, and: + * i) the router has no IPv4 Dir address. + * or + * ii) our preference is for IPv6 Dir addresses. + * + * If there is no node, use fascist_firewall_prefer_ipv6_dirport(). + */ +int +node_ipv6_dir_preferred(const node_t *node) +{ + const or_options_t *options = get_options(); + tor_addr_port_t ipv4_addr; + node_assert_ok(node); + + /* node->ipv6_preferred is set from fascist_firewall_prefer_ipv6_orport(), + * so we can't use it to determine DirPort IPv6 preference. + * This means that bridge clients will use IPv4 DirPorts by default. + */ + if (!fascist_firewall_use_ipv6(options)) { + return 0; + } else if (node_get_prim_dirport(node, &ipv4_addr) + || fascist_firewall_prefer_ipv6_dirport(get_options())) { + return node_has_ipv6_dirport(node); + } + return 0; +} + +/** Copy the primary (IPv4) Dir port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. Return 0 if a valid address and + * port was copied, else return non-zero.*/ +int +node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out) +{ + node_assert_ok(node); + tor_assert(ap_out); + + RETURN_IPV4_AP(node->ri, dir_port, ap_out); + RETURN_IPV4_AP(node->rs, dir_port, ap_out); + /* Microdescriptors only have an IPv6 address */ + + return -1; +} + +#undef RETURN_IPV4_AP + +/** Copy the preferred Dir port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out) +{ + tor_assert(ap_out); + + if (node_ipv6_dir_preferred(node)) { + node_get_pref_ipv6_dirport(node, ap_out); + } else { + /* the primary DirPort is always on IPv4 */ + node_get_prim_dirport(node, ap_out); + } +} + +/** Copy the preferred IPv6 Dir port (IP address and TCP port) for + * <b>node</b> into *<b>ap_out</b>. */ +void +node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) +{ + node_assert_ok(node); + tor_assert(ap_out); + + /* Check if the address or port are valid, and try another alternative if + * they are not. Note that microdescriptors have no dir_port. */ + + /* Assume IPv4 and IPv6 dirports are the same */ + if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr, + node->ri->dir_port, 0)) { + tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); + ap_out->port = node->ri->dir_port; + } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr, + node->rs->dir_port, 0)) { tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); - ap_out->port = node->rs->ipv6_orport; + ap_out->port = node->rs->dir_port; + } else { + tor_addr_make_null(&ap_out->addr, AF_INET6); + ap_out->port = 0; } } @@ -1021,7 +1210,7 @@ nodelist_refresh_countries(void) /** Return true iff router1 and router2 have similar enough network addresses * that we should treat them as being in the same family */ -static INLINE int +static inline int addrs_in_same_network_family(const tor_addr_t *a1, const tor_addr_t *a2) { @@ -1045,7 +1234,7 @@ node_nickname_matches(const node_t *node, const char *nickname) } /** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */ -static INLINE int +static inline int node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node) { if (!lst) return 0; diff --git a/src/or/nodelist.h b/src/or/nodelist.h index a131e0dd4e..71a91e107f 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -55,10 +55,20 @@ void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); time_t node_get_published_on(const node_t *node); const smartlist_t *node_get_declared_family(const node_t *node); -int node_ipv6_preferred(const node_t *node); + +int node_has_ipv6_addr(const node_t *node); +int node_has_ipv6_orport(const node_t *node); +int node_has_ipv6_dirport(const node_t *node); +/* Deprecated - use node_ipv6_or_preferred or node_ipv6_dir_preferred */ +#define node_ipv6_preferred(node) node_ipv6_or_preferred(node) +int node_ipv6_or_preferred(const node_t *node); int node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_orport(const node_t *node, tor_addr_port_t *ap_out); void node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out); +int node_ipv6_dir_preferred(const node_t *node); +int node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_dirport(const node_t *node, tor_addr_port_t *ap_out); +void node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out); int node_has_curve25519_onion_key(const node_t *node); MOCK_DECL(smartlist_t *, nodelist_get_list, (void)); diff --git a/src/or/ntmain.c b/src/or/ntmain.c index 833d870041..ded0e0d307 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -1,8 +1,16 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file ntmain.c + * + * \brief Entry points for running/configuring Tor as Windows Service. + */ + +#ifdef _WIN32 + #include "or.h" #include "config.h" #include "main.h" @@ -315,8 +323,10 @@ nt_service_main(void) case CMD_HASH_PASSWORD: case CMD_VERIFY_CONFIG: case CMD_DUMP_CONFIG: + case CMD_KEYGEN: log_err(LD_CONFIG, "Unsupported command (--list-fingerprint, " - "--hash-password, or --verify-config) in NT service."); + "--hash-password, --keygen, --dump-config, or --verify-config) " + "in NT service."); break; case CMD_RUN_UNITTESTS: default: @@ -762,3 +772,5 @@ nt_service_parse_options(int argc, char **argv, int *should_exit) return 0; } +#endif + diff --git a/src/or/ntmain.h b/src/or/ntmain.h index eb55a296f6..31bf38c62c 100644 --- a/src/or/ntmain.h +++ b/src/or/ntmain.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/onion.c b/src/or/onion.c index 4864792511..d6ef3673dd 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/onion.h b/src/or/onion.h index 45454f480d..0275fa00d2 100644 --- a/src/or/onion.h +++ b/src/or/onion.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c index 7584112570..1f79860596 100644 --- a/src/or/onion_fast.c +++ b/src/or/onion_fast.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -30,10 +30,7 @@ fast_onionskin_create(fast_handshake_state_t **handshake_state_out, { fast_handshake_state_t *s; *handshake_state_out = s = tor_malloc(sizeof(fast_handshake_state_t)); - if (crypto_rand((char*)s->state, sizeof(s->state)) < 0) { - tor_free(s); - return -1; - } + crypto_rand((char*)s->state, sizeof(s->state)); memcpy(handshake_out, s->state, DIGEST_LEN); return 0; } @@ -56,8 +53,7 @@ fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */ size_t out_len; int r = -1; - if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0) - return -1; + crypto_rand((char*)handshake_reply_out, DIGEST_LEN); memcpy(tmp, key_in, DIGEST_LEN); memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h index d50d503a73..b9626002c3 100644 --- a/src/or/onion_fast.h +++ b/src/or/onion_fast.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c index 539f06f61f..9f97a4cfbe 100644 --- a/src/or/onion_ntor.c +++ b/src/or/onion_ntor.c @@ -1,6 +1,12 @@ -/* Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file onion_ntor.c + * + * \brief Implementation for the ntor handshake. + */ + #include "orconfig.h" #define ONION_NTOR_PRIVATE diff --git a/src/or/onion_ntor.h b/src/or/onion_ntor.h index 0a39c1de16..f637b437fd 100644 --- a/src/or/onion_ntor.h +++ b/src/or/onion_ntor.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_ONION_NTOR_H diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c index 487cbeec04..bfd472351f 100644 --- a/src/or/onion_tap.c +++ b/src/or/onion_tap.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/onion_tap.h b/src/or/onion_tap.h index c548b3d99f..a2880f6e98 100644 --- a/src/or/onion_tap.h +++ b/src/or/onion_tap.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/or.h b/src/or/or.h index 0d81b54d94..da84128530 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -22,13 +22,6 @@ #endif #endif -#ifdef _WIN32 -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0501 -#endif -#define WIN32_LEAN_AND_MEAN -#endif - #ifdef HAVE_UNISTD_H #include <unistd.h> #endif @@ -74,6 +67,7 @@ #endif #ifdef _WIN32 +#include <winsock2.h> #include <io.h> #include <process.h> #include <direct.h> @@ -87,8 +81,9 @@ #endif #include "crypto.h" +#include "crypto_format.h" #include "tortls.h" -#include "../common/torlog.h" +#include "torlog.h" #include "container.h" #include "torgzip.h" #include "address.h" @@ -96,7 +91,9 @@ #include "ht.h" #include "replaycache.h" #include "crypto_curve25519.h" +#include "crypto_ed25519.h" #include "tor_queue.h" +#include "util_format.h" /* These signals are defined to help handle_control_signal work. */ @@ -461,9 +458,11 @@ typedef enum { #define CIRCUIT_PURPOSE_OR_MIN_ 1 /** OR-side circuit purpose: normal circuit, at OR. */ #define CIRCUIT_PURPOSE_OR 1 -/** OR-side circuit purpose: At OR, from Bob, waiting for intro from Alices. */ +/** OR-side circuit purpose: At OR, from the service, waiting for intro from + * clients. */ #define CIRCUIT_PURPOSE_INTRO_POINT 2 -/** OR-side circuit purpose: At OR, from Alice, waiting for Bob. */ +/** OR-side circuit purpose: At OR, from the client, waiting for the service. + */ #define CIRCUIT_PURPOSE_REND_POINT_WAITING 3 /** OR-side circuit purpose: At OR, both circuits have this purpose. */ #define CIRCUIT_PURPOSE_REND_ESTABLISHED 4 @@ -482,43 +481,47 @@ typedef enum { * to becoming open, or they are open and have sent the * establish_rendezvous cell but haven't received an ack. * circuits that are c_rend_ready are open and have received a - * rend ack, but haven't heard from bob yet. if they have a + * rend ack, but haven't heard from the service yet. if they have a * buildstate->pending_final_cpath then they're expecting a - * cell from bob, else they're not. + * cell from the service, else they're not. * circuits that are c_rend_ready_intro_acked are open, and * some intro circ has sent its intro and received an ack. * circuits that are c_rend_joined are open, have heard from - * bob, and are talking to him. + * the service, and are talking to it. */ /** Client-side circuit purpose: Normal circuit, with cpath. */ #define CIRCUIT_PURPOSE_C_GENERAL 5 -/** Client-side circuit purpose: at Alice, connecting to intro point. */ +/** Client-side circuit purpose: at the client, connecting to intro point. */ #define CIRCUIT_PURPOSE_C_INTRODUCING 6 -/** Client-side circuit purpose: at Alice, sent INTRODUCE1 to intro point, +/** Client-side circuit purpose: at the client, sent INTRODUCE1 to intro point, * waiting for ACK/NAK. */ #define CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT 7 -/** Client-side circuit purpose: at Alice, introduced and acked, closing. */ +/** Client-side circuit purpose: at the client, introduced and acked, closing. + */ #define CIRCUIT_PURPOSE_C_INTRODUCE_ACKED 8 -/** Client-side circuit purpose: at Alice, waiting for ack. */ +/** Client-side circuit purpose: at the client, waiting for ack. */ #define CIRCUIT_PURPOSE_C_ESTABLISH_REND 9 -/** Client-side circuit purpose: at Alice, waiting for Bob. */ +/** Client-side circuit purpose: at the client, waiting for the service. */ #define CIRCUIT_PURPOSE_C_REND_READY 10 -/** Client-side circuit purpose: at Alice, waiting for Bob, INTRODUCE - * has been acknowledged. */ +/** Client-side circuit purpose: at the client, waiting for the service, + * INTRODUCE has been acknowledged. */ #define CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED 11 -/** Client-side circuit purpose: at Alice, rendezvous established. */ +/** Client-side circuit purpose: at the client, rendezvous established. */ #define CIRCUIT_PURPOSE_C_REND_JOINED 12 /** This circuit is used for build time measurement only */ #define CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT 13 #define CIRCUIT_PURPOSE_C_MAX_ 13 -/** Hidden-service-side circuit purpose: at Bob, waiting for introductions. */ +/** Hidden-service-side circuit purpose: at the service, waiting for + * introductions. */ #define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 14 -/** Hidden-service-side circuit purpose: at Bob, successfully established - * intro. */ +/** Hidden-service-side circuit purpose: at the service, successfully + * established intro. */ #define CIRCUIT_PURPOSE_S_INTRO 15 -/** Hidden-service-side circuit purpose: at Bob, connecting to rend point. */ +/** Hidden-service-side circuit purpose: at the service, connecting to rend + * point. */ #define CIRCUIT_PURPOSE_S_CONNECT_REND 16 -/** Hidden-service-side circuit purpose: at Bob, rendezvous established. */ +/** Hidden-service-side circuit purpose: at the service, rendezvous + * established. */ #define CIRCUIT_PURPOSE_S_REND_JOINED 17 /** A testing circuit; not meant to be used for actual traffic. */ #define CIRCUIT_PURPOSE_TESTING 18 @@ -793,17 +796,34 @@ typedef struct rend_data_t { /** Onion address (without the .onion part) that a client requests. */ char onion_address[REND_SERVICE_ID_LEN_BASE32+1]; + /** Descriptor ID for each replicas computed from the onion address. If + * the onion address is empty, this array MUST be empty. We keep them so + * we know when to purge our entry in the last hsdir request table. */ + char descriptor_id[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN]; + /** (Optional) descriptor cookie that is used by a client. */ char descriptor_cookie[REND_DESC_COOKIE_LEN]; /** Authorization type for accessing a service used by a client. */ rend_auth_type_t auth_type; + /** Descriptor ID for a client request. The control port command HSFETCH + * uses this. It's set if the descriptor query should only use this + * descriptor ID. */ + char desc_id_fetch[DIGEST_LEN]; + /** Hash of the hidden service's PK used by a service. */ char rend_pk_digest[DIGEST_LEN]; /** Rendezvous cookie used by both, client and service. */ char rend_cookie[REND_COOKIE_LEN]; + + /** List of HSDir fingerprints on which this request has been sent to. + * This contains binary identity digest of the directory. */ + smartlist_t *hsdirs_fp; + + /** Number of streams associated with this rendezvous circuit. */ + int nr_streams; } rend_data_t; /** Time interval for tracking replays of DH public keys received in @@ -901,18 +921,18 @@ typedef enum { #define VAR_CELL_MAX_HEADER_SIZE 7 static int get_cell_network_size(int wide_circ_ids); -static INLINE int get_cell_network_size(int wide_circ_ids) +static inline int get_cell_network_size(int wide_circ_ids) { return wide_circ_ids ? CELL_MAX_NETWORK_SIZE : CELL_MAX_NETWORK_SIZE - 2; } static int get_var_cell_header_size(int wide_circ_ids); -static INLINE int get_var_cell_header_size(int wide_circ_ids) +static inline int get_var_cell_header_size(int wide_circ_ids) { return wide_circ_ids ? VAR_CELL_MAX_HEADER_SIZE : VAR_CELL_MAX_HEADER_SIZE - 2; } static int get_circ_id_size(int wide_circ_ids); -static INLINE int get_circ_id_size(int wide_circ_ids) +static inline int get_circ_id_size(int wide_circ_ids) { return wide_circ_ids ? 4 : 2; } @@ -1141,6 +1161,8 @@ typedef struct entry_port_cfg_t { /** When both no-auth and user/pass are advertised by a SOCKS client, select * no-auth. */ unsigned int socks_prefer_no_auth : 1; + /** When ISO_SOCKSAUTH is in use, Keep-Alive circuits indefinitely. */ + unsigned int socks_iso_keep_alive : 1; /* Client port types only: */ unsigned int ipv4_traffic : 1; @@ -1286,7 +1308,7 @@ typedef struct connection_t { * marked.) */ const char *marked_for_close_file; /**< For debugging: in which file were * we marked for close? */ - char *address; /**< FQDN (or IP) of the guy on the other end. + char *address; /**< FQDN (or IP) of the other end. * strdup into this, because free_connection() frees it. */ /** Another connection that's connected to this one in lieu of a socket. */ struct connection_t *linked_conn; @@ -1336,6 +1358,8 @@ typedef struct listener_connection_t { * in the v3 handshake. The subject key must be a 1024-bit RSA key; it * must be signed by the identity key */ #define OR_CERT_TYPE_AUTH_1024 3 +/* DOCDOC */ +#define OR_CERT_TYPE_RSA_ED_CROSSCERT 7 /**@}*/ /** The one currently supported type of AUTHENTICATE cell. It contains @@ -1411,9 +1435,9 @@ typedef struct or_handshake_state_t { * @{ */ /** The cert for the key that's supposed to sign the AUTHENTICATE cell */ - tor_cert_t *auth_cert; + tor_x509_cert_t *auth_cert; /** A self-signed identity certificate */ - tor_cert_t *id_cert; + tor_x509_cert_t *id_cert; /**@}*/ } or_handshake_state_t; @@ -1628,6 +1652,13 @@ typedef struct entry_connection_t { * request that we're going to try to answer. */ struct evdns_server_request *dns_server_request; +#define DEBUGGING_17659 + +#ifdef DEBUGGING_17659 + uint16_t marked_pending_circ_line; + const char *marked_pending_circ_file; +#endif + #define NUM_CIRCUITS_LAUNCHED_THRESHOLD 10 /** Number of times we've launched a circuit to handle this stream. If * it gets too high, that could indicate an inconsistency between our @@ -1731,6 +1762,9 @@ typedef struct control_connection_t { * connection. */ unsigned int is_owning_control_connection:1; + /** List of ephemeral onion services belonging to this connection. */ + smartlist_t *ephemeral_onion_services; + /** If we have sent an AUTHCHALLENGE reply on this connection and * have not received a successful AUTHENTICATE command, points to * the value which the client must send to authenticate itself; @@ -1778,38 +1812,38 @@ static control_connection_t *TO_CONTROL_CONN(connection_t *); * invalid. */ static listener_connection_t *TO_LISTENER_CONN(connection_t *); -static INLINE or_connection_t *TO_OR_CONN(connection_t *c) +static inline or_connection_t *TO_OR_CONN(connection_t *c) { tor_assert(c->magic == OR_CONNECTION_MAGIC); return DOWNCAST(or_connection_t, c); } -static INLINE dir_connection_t *TO_DIR_CONN(connection_t *c) +static inline dir_connection_t *TO_DIR_CONN(connection_t *c) { tor_assert(c->magic == DIR_CONNECTION_MAGIC); return DOWNCAST(dir_connection_t, c); } -static INLINE edge_connection_t *TO_EDGE_CONN(connection_t *c) +static inline edge_connection_t *TO_EDGE_CONN(connection_t *c) { tor_assert(c->magic == EDGE_CONNECTION_MAGIC || c->magic == ENTRY_CONNECTION_MAGIC); return DOWNCAST(edge_connection_t, c); } -static INLINE entry_connection_t *TO_ENTRY_CONN(connection_t *c) +static inline entry_connection_t *TO_ENTRY_CONN(connection_t *c) { tor_assert(c->magic == ENTRY_CONNECTION_MAGIC); return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_.base_); } -static INLINE entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c) +static inline entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *c) { tor_assert(c->base_.magic == ENTRY_CONNECTION_MAGIC); return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_); } -static INLINE control_connection_t *TO_CONTROL_CONN(connection_t *c) +static inline control_connection_t *TO_CONTROL_CONN(connection_t *c) { tor_assert(c->magic == CONTROL_CONNECTION_MAGIC); return DOWNCAST(control_connection_t, c); } -static INLINE listener_connection_t *TO_LISTENER_CONN(connection_t *c) +static inline listener_connection_t *TO_LISTENER_CONN(connection_t *c) { tor_assert(c->magic == LISTENER_CONNECTION_MAGIC); return DOWNCAST(listener_connection_t, c); @@ -1901,7 +1935,7 @@ typedef struct cached_dir_t { size_t dir_len; /**< Length of <b>dir</b> (not counting its NUL). */ size_t dir_z_len; /**< Length of <b>dir_z</b>. */ time_t published; /**< When was this object published. */ - digests_t digests; /**< Digests of this object (networkstatus only) */ + common_digests_t digests; /**< Digests of this object (networkstatus only) */ int refcnt; /**< Reference count for this cached_dir_t. */ } cached_dir_t; @@ -1925,8 +1959,8 @@ typedef enum { } saved_location_t; #define saved_location_bitfield_t ENUM_BF(saved_location_t) -/** Enumeration: what kind of download schedule are we using for a given - * object? */ +/** Enumeration: what directory object is being downloaded? + * This determines which schedule is selected to perform the download. */ typedef enum { DL_SCHED_GENERIC = 0, DL_SCHED_CONSENSUS = 1, @@ -1934,15 +1968,74 @@ typedef enum { } download_schedule_t; #define download_schedule_bitfield_t ENUM_BF(download_schedule_t) +/** Enumeration: is the download schedule for downloading from an authority, + * or from any available directory mirror? + * During bootstrap, "any" means a fallback (or an authority, if there + * are no fallbacks). + * When we have a valid consensus, "any" means any directory server. */ +typedef enum { + DL_WANT_ANY_DIRSERVER = 0, + DL_WANT_AUTHORITY = 1, +} download_want_authority_t; +#define download_want_authority_bitfield_t \ + ENUM_BF(download_want_authority_t) + +/** Enumeration: do we want to increment the schedule position each time a + * connection is attempted (these attempts can be concurrent), or do we want + * to increment the schedule position after a connection fails? */ +typedef enum { + DL_SCHED_INCREMENT_FAILURE = 0, + DL_SCHED_INCREMENT_ATTEMPT = 1, +} download_schedule_increment_t; +#define download_schedule_increment_bitfield_t \ + ENUM_BF(download_schedule_increment_t) + /** Information about our plans for retrying downloads for a downloadable - * object. */ + * directory object. + * Each type of downloadable directory object has a corresponding retry + * <b>schedule</b>, which can be different depending on whether the object is + * being downloaded from an authority or a mirror (<b>want_authority</b>). + * <b>next_attempt_at</b> contains the next time we will attempt to download + * the object. + * For schedules that <b>increment_on</b> failure, <b>n_download_failures</b> + * is used to determine the position in the schedule. (Each schedule is a + * smartlist of integer delays, parsed from a CSV option.) Every time a + * connection attempt fails, <b>n_download_failures</b> is incremented, + * the new delay value is looked up from the schedule, and + * <b>next_attempt_at</b> is set delay seconds from the time the previous + * connection failed. Therefore, at most one failure-based connection can be + * in progress for each download_status_t. + * For schedules that <b>increment_on</b> attempt, <b>n_download_attempts</b> + * is used to determine the position in the schedule. Every time a + * connection attempt is made, <b>n_download_attempts</b> is incremented, + * the new delay value is looked up from the schedule, and + * <b>next_attempt_at</b> is set delay seconds from the time the previous + * connection was attempted. Therefore, multiple concurrent attempted-based + * connections can be in progress for each download_status_t. + * After an object is successfully downloaded, any other concurrent connections + * are terminated. A new schedule which starts at position 0 is used for + * subsequent downloads of the same object. + */ typedef struct download_status_t { - time_t next_attempt_at; /**< When should we try downloading this descriptor + time_t next_attempt_at; /**< When should we try downloading this object * again? */ - uint8_t n_download_failures; /**< Number of failures trying to download the - * most recent descriptor. */ - download_schedule_bitfield_t schedule : 8; - + uint8_t n_download_failures; /**< Number of failed downloads of the most + * recent object, since the last success. */ + uint8_t n_download_attempts; /**< Number of (potentially concurrent) attempts + * to download the most recent object, since + * the last success. */ + download_schedule_bitfield_t schedule : 8; /**< What kind of object is being + * downloaded? This determines the + * schedule used for the download. + */ + download_want_authority_bitfield_t want_authority : 1; /**< Is the download + * happening from an authority + * or a mirror? This determines + * the schedule used for the + * download. */ + download_schedule_increment_bitfield_t increment_on : 1; /**< does this + * schedule increment on each attempt, + * or after each failure? */ } download_status_t; /** If n_download_failures is this high, the download can never happen. */ @@ -1972,6 +2065,10 @@ typedef struct signed_descriptor_t { time_t published_on; /** For routerdescs only: digest of the corresponding extrainfo. */ char extra_info_digest[DIGEST_LEN]; + /** For routerdescs only: A SHA256-digest of the extrainfo (if any) */ + char extra_info_digest256[DIGEST256_LEN]; + /** Certificate for ed25519 signing key. */ + struct tor_cert_st *signing_key_cert; /** For routerdescs only: Status of downloading the corresponding * extrainfo. */ download_status_t ei_dl_status; @@ -2020,6 +2117,9 @@ typedef struct { crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */ /** Public curve25519 key for onions */ curve25519_public_key_t *onion_curve25519_pkey; + /** What's the earliest expiration time on all the certs in this + * routerinfo? */ + time_t cert_expiration_time; char *platform; /**< What software/operating system is this OR using? */ @@ -2053,6 +2153,15 @@ typedef struct { * tests for it. */ unsigned int needs_retest_if_added:1; + /** True iff this router included "tunnelled-dir-server" in its descriptor, + * implying it accepts tunnelled directory requests, or it advertised + * dir_port > 0. */ + unsigned int supports_tunnelled_dir_requests:1; + + /** Used during voting to indicate that we should not include an entry for + * this routerinfo. Used only during voting. */ + unsigned int omit_from_vote:1; + /** Tor can use this router for general positions in circuits; we got it * from a directory server as usual, or we're an authority and a server * uploaded it. */ @@ -2079,6 +2188,8 @@ typedef struct { /** Information needed to keep and cache a signed extra-info document. */ typedef struct extrainfo_t { signed_descriptor_t cache_info; + /** SHA256 digest of this document */ + uint8_t digest256[DIGEST256_LEN]; /** The router's nickname. */ char nickname[MAX_NICKNAME_LEN+1]; /** True iff we found the right key for this extra-info, verified the @@ -2102,7 +2213,7 @@ typedef struct routerstatus_t { /** Digest of the router's most recent descriptor or microdescriptor. * If it's a descriptor, we only use the first DIGEST_LEN bytes. */ char descriptor_digest[DIGEST256_LEN]; - uint32_t addr; /**< IPv4 address for this router. */ + uint32_t addr; /**< IPv4 address for this router, in host order. */ uint16_t or_port; /**< OR port for this router. */ uint16_t dir_port; /**< Directory port for this router. */ tor_addr_t ipv6_addr; /**< IPv6 address for this router. */ @@ -2126,14 +2237,14 @@ typedef struct routerstatus_t { * an exit node. */ unsigned int is_hs_dir:1; /**< True iff this router is a v2-or-later hidden * service directory. */ + unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort + * or it claims to accept tunnelled dir requests. + */ /** True iff we know version info for this router. (i.e., a "v" entry was * included.) We'll replace all these with a big tor_version_t or a char[] * if the number of traits we care about ever becomes incredibly big. */ unsigned int version_known:1; - /** True iff this router is a version that, if it caches directory info, - * we can get microdescriptors from. */ - unsigned int version_supports_microdesc_cache:1; /** True iff this router has a version that allows it to accept EXTEND2 * cells */ unsigned int version_supports_extend2_cells:1; @@ -2228,7 +2339,9 @@ typedef struct microdesc_t { crypto_pk_t *onion_pkey; /** As routerinfo_t.onion_curve25519_pkey */ curve25519_public_key_t *onion_curve25519_pkey; - /** As routerinfo_t.ipv6_add */ + /** Ed25519 identity key, if included. */ + ed25519_public_key_t *ed25519_identity_pkey; + /** As routerinfo_t.ipv6_addr */ tor_addr_t ipv6_addr; /** As routerinfo_t.ipv6_orport */ uint16_t ipv6_orport; @@ -2246,7 +2359,7 @@ typedef struct microdesc_t { * Specifically, a node_t is a Tor router as we are using it: a router that * we are considering for circuits, connections, and so on. A node_t is a * thin wrapper around the routerstatus, routerinfo, and microdesc for a - * single wrapper, and provides a consistent interface for all of them. + * single router, and provides a consistent interface for all of them. * * Also, a node_t has mutable state. While a routerinfo, a routerstatus, * and a microdesc have[*] only the information read from a router @@ -2303,7 +2416,8 @@ typedef struct node_t { /* Local info: derived. */ - /** True if the IPv6 OR port is preferred over the IPv4 OR port. */ + /** True if the IPv6 OR port is preferred over the IPv4 OR port. + * XX/teor - can this become out of date if the torrc changes? */ unsigned int ipv6_preferred:1; /** According to the geoip db what country is this router in? */ @@ -2342,9 +2456,18 @@ typedef struct vote_routerstatus_t { char *version; /**< The version that the authority says this router is * running. */ unsigned int has_measured_bw:1; /**< The vote had a measured bw */ + /** True iff the vote included an entry for ed25519 ID, or included + * "id ed25519 none" to indicate that there was no ed25519 ID. */ + unsigned int has_ed25519_listing:1; + /** True if the Ed25519 listing here is the consensus-opinion for the + * Ed25519 listing; false if there was no consensus on Ed25519 key status, + * or if this VRS doesn't reflect it. */ + unsigned int ed25519_reflects_consensus:1; uint32_t measured_bw_kb; /**< Measured bandwidth (capacity) of the router */ /** The hash or hashes that the authority claims this microdesc has. */ vote_microdesc_hash_t *microdesc; + /** Ed25519 identity for this router, or zero if it has none. */ + uint8_t ed25519_id[ED25519_PUBKEY_LEN]; } vote_routerstatus_t; /** A signature of some document by an authority. */ @@ -2457,7 +2580,7 @@ typedef struct networkstatus_t { struct authority_cert_t *cert; /**< Vote only: the voter's certificate. */ /** Digests of this document, as signed. */ - digests_t digests; + common_digests_t digests; /** List of router statuses, sorted by identity digest. For a vote, * the elements are vote_routerstatus_t; for a consensus, the elements @@ -2844,6 +2967,11 @@ typedef struct circuit_t { * circuits entered certain states. This usage probably won't * interfere with this field's primary purpose, but we should * document it more thoroughly to make sure of that. + * + * XXX027 The SocksPort option KeepaliveIsolateSOCKSAuth will artificially + * adjust this value forward each time a suitable stream is attached to an + * already constructed circuit, potentially keeping the circuit alive + * indefinitely. */ time_t timestamp_dirty; @@ -2852,6 +2980,14 @@ typedef struct circuit_t { * where this circuit was marked.) */ const char *marked_for_close_file; /**< For debugging: in which file was this * circuit marked for close? */ + /** For what reason (See END_CIRC_REASON...) is this circuit being closed? + * This field is set in circuit_mark_for_close and used later in + * circuit_about_to_free. */ + uint16_t marked_for_close_reason; + /** As marked_for_close_reason, but reflects the underlying reason for + * closing this circuit. + */ + uint16_t marked_for_close_orig_reason; /** Unique ID for measuring tunneled network status requests. */ uint64_t dirreq_id; @@ -3241,27 +3377,27 @@ static const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT(const circuit_t *); /** Return 1 iff <b>node</b> has Exit flag and no BadExit flag. * Otherwise, return 0. */ -static INLINE int node_is_good_exit(const node_t *node) +static inline int node_is_good_exit(const node_t *node) { return node->is_exit && ! node->is_bad_exit; } -static INLINE or_circuit_t *TO_OR_CIRCUIT(circuit_t *x) +static inline or_circuit_t *TO_OR_CIRCUIT(circuit_t *x) { tor_assert(x->magic == OR_CIRCUIT_MAGIC); return DOWNCAST(or_circuit_t, x); } -static INLINE const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x) +static inline const or_circuit_t *CONST_TO_OR_CIRCUIT(const circuit_t *x) { tor_assert(x->magic == OR_CIRCUIT_MAGIC); return DOWNCAST(or_circuit_t, x); } -static INLINE origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x) +static inline origin_circuit_t *TO_ORIGIN_CIRCUIT(circuit_t *x) { tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC); return DOWNCAST(origin_circuit_t, x); } -static INLINE const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT( +static inline const origin_circuit_t *CONST_TO_ORIGIN_CIRCUIT( const circuit_t *x) { tor_assert(x->magic == ORIGIN_CIRCUIT_MAGIC); @@ -3325,6 +3461,10 @@ typedef struct port_cfg_t { uint8_t type; /**< One of CONN_TYPE_*_LISTENER */ unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */ + unsigned is_group_writable : 1; + unsigned is_world_writable : 1; + unsigned relax_dirmode_check : 1; + entry_port_cfg_t entry_cfg; server_port_cfg_t server_cfg; @@ -3368,7 +3508,8 @@ typedef struct { /** What should the tor process actually do? */ enum { CMD_RUN_TOR=0, CMD_LIST_FINGERPRINT, CMD_HASH_PASSWORD, - CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG + CMD_VERIFY_CONFIG, CMD_RUN_UNITTESTS, CMD_DUMP_CONFIG, + CMD_KEYGEN } command; char *command_arg; /**< Argument for command-line option. */ @@ -3380,15 +3521,15 @@ typedef struct { * each log message occurs? */ int TruncateLogFile; /**< Boolean: Should we truncate the log file before we start writing? */ + char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ char *DebugLogFile; /**< Where to send verbose log messages. */ char *DataDirectory; /**< OR only: where to store long-term data. */ + int DataDirectoryGroupReadable; /**< Boolean: Is the DataDirectory g+r? */ char *Nickname; /**< OR only: nickname of this onion router. */ char *Address; /**< OR only: configured address for this onion router. */ char *PidFile; /**< Where to store PID of Tor process. */ - int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/ - routerset_t *ExitNodes; /**< Structure containing nicknames, digests, * country codes and IP address patterns of ORs to * consider as exits. */ @@ -3560,10 +3701,7 @@ typedef struct { int PublishHidServDescriptors; int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */ int FetchHidServDescriptors; /**< and hidden service descriptors? */ - int HidServDirectoryV2; /**< Do we participate in the HS DHT? */ - int VoteOnHidServDirectoriesV2; /**< As a directory authority, vote on - * assignment of the HSDir flag? */ int MinUptimeHidServDirectoryV2; /**< As directory authority, accept hidden * service directories after what time? */ @@ -3649,7 +3787,7 @@ typedef struct { * and try a new circuit if the stream has been * waiting for this many seconds. If zero, use * our default internal timeout schedule. */ - int MaxOnionQueueDelay; /**<DOCDOC*/ + int MaxOnionQueueDelay; /*< DOCDOC */ int NewCircuitPeriod; /**< How long do we use a circuit before building * a new one? */ int MaxCircuitDirtiness; /**< Never use circs that were first used more than @@ -3709,6 +3847,8 @@ typedef struct { /** List of fallback directory servers */ config_line_t *FallbackDir; + /** Whether to use the default hard-coded FallbackDirs */ + int UseDefaultFallbackDirs; /** Weight to apply to all directory authority rates if considering them * along with fallbackdirs */ @@ -3751,6 +3891,7 @@ typedef struct { * number of servers per IP address shared * with an authority. */ int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */ + int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */ /** If non-zero, always vote the Fast flag for any relay advertising * this amount of capacity or more. */ @@ -3767,9 +3908,11 @@ typedef struct { * hibernate." */ /** How do we determine when our AccountingMax has been reached? * "max" for when in or out reaches AccountingMax - * "sum for when in plus out reaches AccountingMax */ + * "sum" for when in plus out reaches AccountingMax + * "in" for when in reaches AccountingMax + * "out" for when out reaches AccountingMax */ char *AccountingRule_option; - enum { ACCT_MAX, ACCT_SUM } AccountingRule; + enum { ACCT_MAX, ACCT_SUM, ACCT_IN, ACCT_OUT } AccountingRule; /** Base64-encoded hash of accepted passwords for the control system. */ config_line_t *HashedControlPassword; @@ -3843,6 +3986,10 @@ typedef struct { /** Should we fetch our dir info at the start of the consensus period? */ int FetchDirInfoExtraEarly; + int DirCache; /**< Cache all directory documents and accept requests via + * tunnelled dir conns from clients. If 1, enabled (default); + * If 0, disabled. */ + char *VirtualAddrNetworkIPv4; /**< Address and mask to hand out for virtual * MAPADDRESS requests for IPv4 addresses */ char *VirtualAddrNetworkIPv6; /**< Address and mask to hand out for virtual @@ -3943,12 +4090,24 @@ typedef struct { * over randomly chosen exits. */ int ClientRejectInternalAddresses; - /** If true, clients may connect over IPv6. XXX we don't really - enforce this -- clients _may_ set up outgoing IPv6 connections - even when this option is not set. */ + /** If true, clients may connect over IPv4. If false, they will avoid + * connecting over IPv4. We enforce this for OR and Dir connections. */ + int ClientUseIPv4; + /** If true, clients may connect over IPv6. If false, they will avoid + * connecting over IPv4. We enforce this for OR and Dir connections. + * Use fascist_firewall_use_ipv6() instead of accessing this value + * directly. */ int ClientUseIPv6; - /** If true, prefer an IPv6 OR port over an IPv4 one. */ + /** If true, prefer an IPv6 OR port over an IPv4 one for entry node + * connections. If auto, bridge clients prefer IPv6, and other clients + * prefer IPv4. Use node_ipv6_or_preferred() instead of accessing this value + * directly. */ int ClientPreferIPv6ORPort; + /** If true, prefer an IPv6 directory port over an IPv4 one for direct + * directory connections. If auto, bridge clients prefer IPv6, and other + * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of + * accessing this value directly. */ + int ClientPreferIPv6DirPort; /** The length of time that we think a consensus should be fresh. */ int V3AuthVotingInterval; @@ -3974,7 +4133,7 @@ typedef struct { char *ConsensusParams; /** Authority only: minimum number of measured bandwidths we must see - * before we only beliee measured bandwidths to assign flags. */ + * before we only believe measured bandwidths to assign flags. */ int MinMeasuredBWsForAuthToIgnoreAdvertised; /** The length of time that we think an initial consensus should be fresh. @@ -4019,6 +4178,36 @@ typedef struct { * on testing networks. */ smartlist_t *TestingClientConsensusDownloadSchedule; + /** Schedule for when clients should download consensuses from authorities + * if they are bootstrapping (that is, they don't have a usable, reasonably + * live consensus). Only used by clients fetching from a list of fallback + * directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + smartlist_t *ClientBootstrapConsensusAuthorityDownloadSchedule; + + /** Schedule for when clients should download consensuses from fallback + * directory mirrors if they are bootstrapping (that is, they don't have a + * usable, reasonably live consensus). Only used by clients fetching from a + * list of fallback directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + smartlist_t *ClientBootstrapConsensusFallbackDownloadSchedule; + + /** Schedule for when clients should download consensuses from authorities + * if they are bootstrapping (that is, they don't have a usable, reasonably + * live consensus). Only used by clients which don't have or won't fetch + * from a list of fallback directory mirrors. + * + * This schedule is incremented by (potentially concurrent) connection + * attempts, unlike other schedules, which are incremented by connection + * failures. Only altered on testing networks. */ + smartlist_t *ClientBootstrapConsensusAuthorityOnlyDownloadSchedule; + /** Schedule for when clients should download bridge descriptors. Only * altered on testing networks. */ smartlist_t *TestingBridgeDownloadSchedule; @@ -4036,6 +4225,21 @@ typedef struct { * up? Only altered on testing networks. */ int TestingConsensusMaxDownloadTries; + /** How many times will a client try to fetch a consensus while + * bootstrapping using a list of fallback directories, before it gives up? + * Only altered on testing networks. */ + int ClientBootstrapConsensusMaxDownloadTries; + + /** How many times will a client try to fetch a consensus while + * bootstrapping using only a list of authorities, before it gives up? + * Only altered on testing networks. */ + int ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries; + + /** How many simultaneous in-progress connections will we make when trying + * to fetch a consensus before we wait for one to complete, timeout, or + * error out? Only altered on testing networks. */ + int ClientBootstrapConsensusMaxInProgressTries; + /** How many times will we try to download a router's descriptor before * giving up? Only altered on testing networks. */ int TestingDescriptorMaxDownloadTries; @@ -4062,15 +4266,18 @@ typedef struct { /** Relays in a testing network which should be voted Exit * regardless of exit policy. */ routerset_t *TestingDirAuthVoteExit; + int TestingDirAuthVoteExitIsStrict; /** Relays in a testing network which should be voted Guard * regardless of uptime and bandwidth. */ routerset_t *TestingDirAuthVoteGuard; + int TestingDirAuthVoteGuardIsStrict; /** Relays in a testing network which should be voted HSDir - * regardless of uptime and ORPort connectivity. + * regardless of uptime and DirPort. * Respects VoteOnHidServDirectoriesV2. */ routerset_t *TestingDirAuthVoteHSDir; + int TestingDirAuthVoteHSDirIsStrict; /** Enable CONN_BW events. Only altered on testing networks. */ int TestingEnableConnBwEvent; @@ -4246,6 +4453,36 @@ typedef struct { * XXXX Eventually, the default will be 0. */ int ExitRelay; + /** For how long (seconds) do we declare our singning keys to be valid? */ + int SigningKeyLifetime; + /** For how long (seconds) do we declare our link keys to be valid? */ + int TestingLinkCertLifetime; + /** For how long (seconds) do we declare our auth keys to be valid? */ + int TestingAuthKeyLifetime; + + /** How long before signing keys expire will we try to make a new one? */ + int TestingSigningKeySlop; + /** How long before link keys expire will we try to make a new one? */ + int TestingLinkKeySlop; + /** How long before auth keys expire will we try to make a new one? */ + int TestingAuthKeySlop; + + /** Force use of offline master key features: never generate a master + * ed25519 identity key except from tor --keygen */ + int OfflineMasterKey; + + enum { + FORCE_PASSPHRASE_AUTO=0, + FORCE_PASSPHRASE_ON, + FORCE_PASSPHRASE_OFF + } keygen_force_passphrase; + int use_keygen_passphrase_fd; + int keygen_passphrase_fd; + int change_key_passphrase; + char *master_key_fname; + + /** Autobool: Do we try to retain capabilities if we can? */ + int KeepBindCapabilities; } or_options_t; /** Persistent state for an onion router, as saved to disk. */ @@ -4318,7 +4555,7 @@ typedef struct { /** Change the next_write time of <b>state</b> to <b>when</b>, unless the * state is already scheduled to be written to disk earlier than <b>when</b>. */ -static INLINE void or_state_mark_dirty(or_state_t *state, time_t when) +static inline void or_state_mark_dirty(or_state_t *state, time_t when) { if (state->next_write > when) state->next_write = when; @@ -4810,12 +5047,13 @@ typedef struct rend_encoded_v2_service_descriptor_t { * introduction point. See also rend_intro_point_t.unreachable_count. */ #define MAX_INTRO_POINT_REACHABILITY_FAILURES 5 -/** The maximum number of distinct INTRODUCE2 cells which a hidden - * service's introduction point will receive before it begins to - * expire. - * - * XXX023 Is this number at all sane? */ -#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384 +/** The minimum and maximum number of distinct INTRODUCE2 cells which a + * hidden service's introduction point will receive before it begins to + * expire. */ +#define INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS 16384 +/* Double the minimum value so the interval is [min, min * 2]. */ +#define INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS \ + (INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS * 2) /** The minimum number of seconds that an introduction point will last * before expiring due to old age. (If it receives @@ -4830,6 +5068,11 @@ typedef struct rend_encoded_v2_service_descriptor_t { * XXX023 Should this be configurable? */ #define INTRO_POINT_LIFETIME_MAX_SECONDS (24*60*60) +/** The maximum number of circuit creation retry we do to an intro point + * before giving up. We try to reuse intro point that fails during their + * lifetime so this is a hard limit on the amount of time we do that. */ +#define MAX_INTRO_POINT_CIRCUIT_RETRIES 3 + /** Introduction point information. Used both in rend_service_t (on * the service side) and in rend_service_descriptor_t (on both the * client and service side). */ @@ -4854,11 +5097,6 @@ typedef struct rend_intro_point_t { * included in the last HS descriptor we generated. */ unsigned int listed_in_last_desc : 1; - /** (Service side only) Flag indicating that - * rend_service_note_removing_intro_point has been called for this - * intro point. */ - unsigned int rend_service_note_removing_intro_point_called : 1; - /** (Service side only) A replay cache recording the RSA-encrypted parts * of INTRODUCE2 cells this intro point's circuit has received. This is * used to prevent replay attacks. */ @@ -4869,6 +5107,12 @@ typedef struct rend_intro_point_t { */ int accepted_introduce2_count; + /** (Service side only) Number of maximum INTRODUCE2 cells that this IP + * will accept. This is a random value between + * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and + * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */ + int max_introductions; + /** (Service side only) The time at which this intro point was first * published, or -1 if this intro point has not yet been * published. */ @@ -4879,15 +5123,16 @@ typedef struct rend_intro_point_t { * point should expire. */ time_t time_to_expire; - /** (Service side only) The time at which we decided that this intro - * point should start expiring, or -1 if this intro point is not yet - * expiring. - * - * This field also serves as a flag to indicate that we have decided - * to expire this intro point, in case intro_point_should_expire_now - * flaps (perhaps due to a clock jump; perhaps due to other - * weirdness, or even a (present or future) bug). */ - time_t time_expiring; + /** (Service side only) The amount of circuit creation we've made to this + * intro point. This is incremented every time we do a circuit relaunch on + * this object which is triggered when the circuit dies but the node is + * still in the consensus. After MAX_INTRO_POINT_CIRCUIT_RETRIES, we give + * up on it. */ + unsigned int circuit_retries; + + /** (Service side only) Set if this intro point has an established circuit + * and unset if it doesn't. */ + unsigned int circuit_established:1; } rend_intro_point_t; #define REND_PROTOCOL_VERSION_BITMASK_WIDTH 16 @@ -4913,15 +5158,6 @@ typedef struct rend_service_descriptor_t { smartlist_t *successful_uploads; } rend_service_descriptor_t; -/** A cached rendezvous descriptor. */ -typedef struct rend_cache_entry_t { - size_t len; /**< Length of <b>desc</b> */ - time_t last_served; /**< When did we last write this one to somebody? - * (HSDir only) */ - char *desc; /**< Service descriptor */ - rend_service_descriptor_t *parsed; /**< Parsed value of 'desc' */ -} rend_cache_entry_t; - /********************************* routerlist.c ***************************/ /** Represents information about a single trusted or fallback directory @@ -4930,9 +5166,13 @@ typedef struct dir_server_t { char *description; char *nickname; char *address; /**< Hostname. */ + /* XX/teor - why do we duplicate the address and port fields here and in + * fake_status? Surely we could just use fake_status (#17867). */ + tor_addr_t ipv6_addr; /**< IPv6 address if present; AF_UNSPEC if not */ uint32_t addr; /**< IPv4 address. */ uint16_t dir_port; /**< Directory port. */ uint16_t or_port; /**< OR port: Used for tunneling connections. */ + uint16_t ipv6_orport; /**< OR port corresponding to ipv6_addr. */ double weight; /** Weight used when selecting this node at random */ char digest[DIGEST_LEN]; /**< Digest of identity key. */ char v3_identity_digest[DIGEST_LEN]; /**< Digest of v3 (authority only, @@ -5013,7 +5253,12 @@ typedef enum { CRN_ALLOW_INVALID = 1<<3, /* XXXX not used, apparently. */ CRN_WEIGHT_AS_EXIT = 1<<5, - CRN_NEED_DESC = 1<<6 + CRN_NEED_DESC = 1<<6, + /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */ + CRN_PREF_ADDR = 1<<7, + /* On clients, only provide nodes that we can connect to directly, based on + * our firewall rules */ + CRN_DIRECT_CONN = 1<<8 } router_crn_flags_t; /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */ @@ -5043,6 +5288,8 @@ typedef enum was_router_added_t { /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ + /* DOCDOC */ + ROUTER_CERTS_EXPIRED = -8 } was_router_added_t; /********************************* routerparse.c ************************/ diff --git a/src/or/periodic.c b/src/or/periodic.c new file mode 100644 index 0000000000..057fcf672e --- /dev/null +++ b/src/or/periodic.c @@ -0,0 +1,126 @@ +/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file periodic.c + * + * \brief Generic backend for handling periodic events. + */ + +#include "or.h" +#include "compat_libevent.h" +#include "config.h" +#include "periodic.h" + +#ifdef HAVE_EVENT2_EVENT_H +#include <event2/event.h> +#else +#include <event.h> +#endif + +/** We disable any interval greater than this number of seconds, on the + * grounds that it is probably an absolute time mistakenly passed in as a + * relative time. + */ +static const int MAX_INTERVAL = 10 * 365 * 86400; + +/** Set the event <b>event</b> to run in <b>next_interval</b> seconds from + * now. */ +static void +periodic_event_set_interval(periodic_event_item_t *event, + time_t next_interval) +{ + tor_assert(next_interval < MAX_INTERVAL); + struct timeval tv; + tv.tv_sec = next_interval; + tv.tv_usec = 0; + event_add(event->ev, &tv); +} + +/** Wraps dispatches for periodic events, <b>data</b> will be a pointer to the + * event that needs to be called */ +static void +periodic_event_dispatch(evutil_socket_t fd, short what, void *data) +{ + (void)fd; + (void)what; + periodic_event_item_t *event = data; + + time_t now = time(NULL); + const or_options_t *options = get_options(); +// log_debug(LD_GENERAL, "Dispatching %s", event->name); + int r = event->fn(now, options); + int next_interval = 0; + + /* update the last run time if action was taken */ + if (r==0) { + log_err(LD_BUG, "Invalid return value for periodic event from %s.", + event->name); + tor_assert(r != 0); + } else if (r > 0) { + event->last_action_time = now; + /* If the event is meant to happen after ten years, that's likely + * a bug, and somebody gave an absolute time rather than an interval. + */ + tor_assert(r < MAX_INTERVAL); + next_interval = r; + } else { + /* no action was taken, it is likely a precondition failed, + * we should reschedule for next second incase the precondition + * passes then */ + next_interval = 1; + } + +// log_debug(LD_GENERAL, "Scheduling %s for %d seconds", event->name, +// next_interval); + struct timeval tv = { next_interval , 0 }; + event_add(event->ev, &tv); +} + +/** Schedules <b>event</b> to run as soon as possible from now. */ +void +periodic_event_reschedule(periodic_event_item_t *event) +{ + periodic_event_set_interval(event, 1); +} + +/** Initializes the libevent backend for a periodic event. */ +void +periodic_event_setup(periodic_event_item_t *event) +{ + if (event->ev) { /* Already setup? This is a bug */ + log_err(LD_BUG, "Initial dispatch should only be done once."); + tor_assert(0); + } + + event->ev = tor_event_new(tor_libevent_get_base(), + -1, 0, + periodic_event_dispatch, + event); + tor_assert(event->ev); +} + +/** Handles initial dispatch for periodic events. It should happen 1 second + * after the events are created to mimic behaviour before #3199's refactor */ +void +periodic_event_launch(periodic_event_item_t *event) +{ + if (! event->ev) { /* Not setup? This is a bug */ + log_err(LD_BUG, "periodic_event_launch without periodic_event_setup"); + tor_assert(0); + } + + // Initial dispatch + periodic_event_dispatch(-1, EV_TIMEOUT, event); +} + +/** Release all storage associated with <b>event</b> */ +void +periodic_event_destroy(periodic_event_item_t *event) +{ + if (!event) + return; + tor_event_free(event->ev); + event->last_action_time = 0; +} + diff --git a/src/or/periodic.h b/src/or/periodic.h new file mode 100644 index 0000000000..021bb4ef5c --- /dev/null +++ b/src/or/periodic.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_PERIODIC_H +#define TOR_PERIODIC_H + +#define PERIODIC_EVENT_NO_UPDATE (-1) + +/** Callback function for a periodic event to take action. The return value +* influences the next time the function will get called. Return +* PERIODIC_EVENT_NO_UPDATE to not update <b>last_action_time</b> and be polled +* again in the next second. If a positive value is returned it will update the +* interval time. */ +typedef int (*periodic_event_helper_t)(time_t now, + const or_options_t *options); + +struct event; + +/** A single item for the periodic-events-function table. */ +typedef struct periodic_event_item_t { + periodic_event_helper_t fn; /**< The function to run the event */ + time_t last_action_time; /**< The last time the function did something */ + struct event *ev; /**< Libevent callback we're using to implement this */ + const char *name; /**< Name of the function -- for debug */ +} periodic_event_item_t; + +/** events will get their interval from first execution */ +#define PERIODIC_EVENT(fn) { fn##_callback, 0, NULL, #fn } +#define END_OF_PERIODIC_EVENTS { NULL, 0, NULL, NULL } + +void periodic_event_launch(periodic_event_item_t *event); +void periodic_event_setup(periodic_event_item_t *event); +void periodic_event_destroy(periodic_event_item_t *event); +void periodic_event_reschedule(periodic_event_item_t *event); + +#endif + diff --git a/src/or/policies.c b/src/or/policies.c index 560b8cb4c3..50fec3a773 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -8,9 +8,12 @@ * \brief Code to parse and use address policies and exit policies. **/ +#define POLICIES_PRIVATE + #include "or.h" #include "config.h" #include "dirserv.h" +#include "networkstatus.h" #include "nodelist.h" #include "policies.h" #include "router.h" @@ -62,12 +65,15 @@ static const char *private_nets[] = { NULL }; -static int policies_parse_exit_policy_internal(config_line_t *cfg, - smartlist_t **dest, - int ipv6_exit, - int rejectprivate, - uint32_t local_address, - int add_default_policy); +static int policies_parse_exit_policy_internal( + config_line_t *cfg, + smartlist_t **dest, + int ipv6_exit, + int rejectprivate, + const smartlist_t *configured_addresses, + int reject_interface_addresses, + int reject_configured_port_addresses, + int add_default_policy); /** Replace all "private" entries in *<b>policy</b> with their expanded * equivalents. */ @@ -152,7 +158,7 @@ policy_expand_unspec(smartlist_t **policy) } /** - * Given a linked list of config lines containing "allow" and "deny" + * Given a linked list of config lines containing "accept[6]" and "reject[6]" * tokens, parse them and append the result to <b>dest</b>. Return -1 * if any tokens are malformed (and don't append any), else return 0. * @@ -167,6 +173,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest, smartlist_t *result; smartlist_t *entries; addr_policy_t *item; + int malformed_list; int r = 0; if (!cfg) @@ -179,12 +186,22 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); SMARTLIST_FOREACH_BEGIN(entries, const char *, ent) { log_debug(LD_CONFIG,"Adding new entry '%s'",ent); - item = router_parse_addr_policy_item_from_string(ent, assume_action); + malformed_list = 0; + item = router_parse_addr_policy_item_from_string(ent, assume_action, + &malformed_list); if (item) { smartlist_add(result, item); - } else { - log_warn(LD_CONFIG,"Malformed policy '%s'.", ent); + } else if (malformed_list) { + /* the error is so severe the entire list should be discarded */ + log_warn(LD_CONFIG, "Malformed policy '%s'. Discarding entire policy " + "list.", ent); r = -1; + } else { + /* the error is minor: don't add the item, but keep processing the + * rest of the policies in the list */ + log_debug(LD_CONFIG, "Ignored policy '%s' due to non-fatal error. " + "The remainder of the policy list will be used.", + ent); } } SMARTLIST_FOREACH_END(ent); SMARTLIST_FOREACH(entries, char *, ent, tor_free(ent)); @@ -254,16 +271,76 @@ parse_reachable_addresses(void) "Error parsing ReachableDirAddresses entry; ignoring."); ret = -1; } + + /* We ignore ReachableAddresses for relays */ + if (!server_mode(options)) { + if ((reachable_or_addr_policy + && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC)) + || (reachable_dir_addr_policy + && policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC))) { + log_warn(LD_CONFIG, "Tor cannot connect to the Internet if " + "ReachableAddresses, ReachableORAddresses, or " + "ReachableDirAddresses reject all addresses. Please accept " + "some addresses in these options."); + } else if (options->ClientUseIPv4 == 1 + && ((reachable_or_addr_policy + && policy_is_reject_star(reachable_or_addr_policy, AF_INET)) + || (reachable_dir_addr_policy + && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) { + log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but " + "ReachableAddresses, ReachableORAddresses, or " + "ReachableDirAddresses reject all IPv4 addresses. " + "Tor will not connect using IPv4."); + } else if (fascist_firewall_use_ipv6(options) + && ((reachable_or_addr_policy + && policy_is_reject_star(reachable_or_addr_policy, AF_INET6)) + || (reachable_dir_addr_policy + && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) { + log_warn(LD_CONFIG, "You have configured tor to use IPv6 " + "(ClientUseIPv6 1 or UseBridges 1), but " + "ReachableAddresses, ReachableORAddresses, or " + "ReachableDirAddresses reject all IPv6 addresses. " + "Tor will not connect using IPv6."); + } + } + return ret; } -/** Return true iff the firewall options might block any address:port - * combination. +/* Return true iff ClientUseIPv4 0 or ClientUseIPv6 0 might block any OR or Dir + * address:port combination. */ +static int +firewall_is_fascist_impl(void) +{ + const or_options_t *options = get_options(); + /* Assume every non-bridge relay has an IPv4 address. + * Clients which use bridges may only know the IPv6 address of their + * bridge. */ + return (options->ClientUseIPv4 == 0 + || (!fascist_firewall_use_ipv6(options) + && options->UseBridges == 1)); +} + +/** Return true iff the firewall options, including ClientUseIPv4 0 and + * ClientUseIPv6 0, might block any OR address:port combination. + * Address preferences may still change which address is selected even if + * this function returns false. */ int firewall_is_fascist_or(void) { - return reachable_or_addr_policy != NULL; + return (reachable_or_addr_policy != NULL || firewall_is_fascist_impl()); +} + +/** Return true iff the firewall options, including ClientUseIPv4 0 and + * ClientUseIPv6 0, might block any Dir address:port combination. + * Address preferences may still change which address is selected even if + * this function returns false. + */ +int +firewall_is_fascist_dir(void) +{ + return (reachable_dir_addr_policy != NULL || firewall_is_fascist_impl()); } /** Return true iff <b>policy</b> (possibly NULL) will allow a @@ -301,49 +378,618 @@ addr_policy_permits_address(uint32_t addr, uint16_t port, return addr_policy_permits_tor_addr(&a, port, policy); } -/** Return true iff we think our firewall will let us make an OR connection to - * addr:port. */ -int -fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port) +/** Return true iff we think our firewall will let us make a connection to + * addr:port. + * + * If we are configured as a server, ignore any address family preference and + * just use IPv4. + * Otherwise: + * - return false for all IPv4 addresses: + * - if ClientUseIPv4 is 0, or + * if pref_only and pref_ipv6 are both true; + * - return false for all IPv6 addresses: + * - if fascist_firewall_use_ipv6() is 0, or + * - if pref_only is true and pref_ipv6 is false. + * + * Return false if addr is NULL or tor_addr_is_null(), or if port is 0. */ +STATIC int +fascist_firewall_allows_address(const tor_addr_t *addr, + uint16_t port, + smartlist_t *firewall_policy, + int pref_only, int pref_ipv6) { + const or_options_t *options = get_options(); + const int client_mode = !server_mode(options); + + if (!addr || tor_addr_is_null(addr) || !port) { + return 0; + } + + /* Clients stop using IPv4 if it's disabled. In most cases, clients also + * stop using IPv4 if it's not preferred. + * Servers must have IPv4 enabled and preferred. */ + if (tor_addr_family(addr) == AF_INET && client_mode && + (!options->ClientUseIPv4 || (pref_only && pref_ipv6))) { + return 0; + } + + /* Clients and Servers won't use IPv6 unless it's enabled (and in most + * cases, IPv6 must also be preferred before it will be used). */ + if (tor_addr_family(addr) == AF_INET6 && + (!fascist_firewall_use_ipv6(options) || (pref_only && !pref_ipv6))) { + return 0; + } + return addr_policy_permits_tor_addr(addr, port, - reachable_or_addr_policy); + firewall_policy); +} + +/** Is this client configured to use IPv6? + * Use node_ipv6_or/dir_preferred() when checking a specific node and OR/Dir + * port: it supports bridge client per-node IPv6 preferences. + */ +int +fascist_firewall_use_ipv6(const or_options_t *options) +{ + /* Clients use IPv6 if it's set, or they use bridges, or they don't use + * IPv4 */ + return (options->ClientUseIPv6 == 1 || options->UseBridges == 1 + || options->ClientUseIPv4 == 0); +} + +/** Do we prefer to connect to IPv6, ignoring ClientPreferIPv6ORPort and + * ClientPreferIPv6DirPort? + * If we're unsure, return -1, otherwise, return 1 for IPv6 and 0 for IPv4. + */ +static int +fascist_firewall_prefer_ipv6_impl(const or_options_t *options) +{ + /* + Cheap implementation of config options ClientUseIPv4 & ClientUseIPv6 -- + If we're a server or IPv6 is disabled, use IPv4. + If IPv4 is disabled, use IPv6. + */ + + if (server_mode(options) || !fascist_firewall_use_ipv6(options)) { + return 0; + } + + if (!options->ClientUseIPv4) { + return 1; + } + + return -1; } -/** Return true iff we think our firewall will let us make an OR connection to - * <b>ri</b>. */ +/** Do we prefer to connect to IPv6 ORPorts? + * Use node_ipv6_or_preferred() whenever possible: it supports bridge client + * per-node IPv6 preferences. + */ int -fascist_firewall_allows_or(const routerinfo_t *ri) +fascist_firewall_prefer_ipv6_orport(const or_options_t *options) { - /* XXXX proposal 118 */ - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return fascist_firewall_allows_address_or(&addr, ri->or_port); + int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options); + + if (pref_ipv6 >= 0) { + return pref_ipv6; + } + + /* We can use both IPv4 and IPv6 - which do we prefer? */ + if (options->ClientPreferIPv6ORPort == 1) { + return 1; + } + + return 0; } -/** Return true iff we think our firewall will let us make an OR connection to - * <b>node</b>. */ +/** Do we prefer to connect to IPv6 DirPorts? + * + * (node_ipv6_dir_preferred() doesn't support bridge client per-node IPv6 + * preferences. There's no reason to use it instead of this function.) + */ int -fascist_firewall_allows_node(const node_t *node) +fascist_firewall_prefer_ipv6_dirport(const or_options_t *options) { - if (node->ri) { - return fascist_firewall_allows_or(node->ri); - } else if (node->rs) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, node->rs->addr); - return fascist_firewall_allows_address_or(&addr, node->rs->or_port); + int pref_ipv6 = fascist_firewall_prefer_ipv6_impl(options); + + if (pref_ipv6 >= 0) { + return pref_ipv6; + } + + /* We can use both IPv4 and IPv6 - which do we prefer? */ + if (options->ClientPreferIPv6DirPort == 1) { + return 1; + } + + return 0; +} + +/** Return true iff we think our firewall will let us make a connection to + * addr:port. Uses ReachableORAddresses or ReachableDirAddresses based on + * fw_connection. + * If pref_only is true, return true if addr is in the client's preferred + * address family, which is IPv6 if pref_ipv6 is true, and IPv4 otherwise. + * If pref_only is false, ignore pref_ipv6, and return true if addr is allowed. + */ +int +fascist_firewall_allows_address_addr(const tor_addr_t *addr, uint16_t port, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + if (fw_connection == FIREWALL_OR_CONNECTION) { + return fascist_firewall_allows_address(addr, port, + reachable_or_addr_policy, + pref_only, pref_ipv6); + } else if (fw_connection == FIREWALL_DIR_CONNECTION) { + return fascist_firewall_allows_address(addr, port, + reachable_dir_addr_policy, + pref_only, pref_ipv6); } else { + log_warn(LD_BUG, "Bad firewall_connection_t value %d.", + fw_connection); + return 0; + } +} + +/** Return true iff we think our firewall will let us make a connection to + * addr:port (ap). Uses ReachableORAddresses or ReachableDirAddresses based on + * fw_connection. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static int +fascist_firewall_allows_address_ap(const tor_addr_port_t *ap, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + tor_assert(ap); + return fascist_firewall_allows_address_addr(&ap->addr, ap->port, + fw_connection, pref_only, + pref_ipv6); +} + +/* Return true iff we think our firewall will let us make a connection to + * ipv4h_or_addr:ipv4_or_port. ipv4h_or_addr is interpreted in host order. + * Uses ReachableORAddresses or ReachableDirAddresses based on + * fw_connection. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static int +fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr, + uint16_t ipv4_or_port, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + tor_addr_t ipv4_or_addr; + tor_addr_from_ipv4h(&ipv4_or_addr, ipv4h_or_addr); + return fascist_firewall_allows_address_addr(&ipv4_or_addr, ipv4_or_port, + fw_connection, pref_only, + pref_ipv6); +} + +/** Return true iff we think our firewall will let us make a connection to + * ipv4h_addr/ipv6_addr. Uses ipv4_orport/ipv6_orport/ReachableORAddresses or + * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and + * <b>fw_connection</b>. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static int +fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport, + uint16_t ipv4_dirport, + const tor_addr_t *ipv6_addr, uint16_t ipv6_orport, + uint16_t ipv6_dirport, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + if (fascist_firewall_allows_address_ipv4h(ipv4h_addr, + (fw_connection == FIREWALL_OR_CONNECTION + ? ipv4_orport + : ipv4_dirport), + fw_connection, + pref_only, pref_ipv6)) { return 1; } + + if (fascist_firewall_allows_address_addr(ipv6_addr, + (fw_connection == FIREWALL_OR_CONNECTION + ? ipv6_orport + : ipv6_dirport), + fw_connection, + pref_only, pref_ipv6)) { + return 1; + } + + return 0; +} + +/** Like fascist_firewall_allows_base(), but takes ri. */ +static int +fascist_firewall_allows_ri_impl(const routerinfo_t *ri, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + if (!ri) { + return 0; + } + + /* Assume IPv4 and IPv6 DirPorts are the same */ + return fascist_firewall_allows_base(ri->addr, ri->or_port, ri->dir_port, + &ri->ipv6_addr, ri->ipv6_orport, + ri->dir_port, fw_connection, pref_only, + pref_ipv6); +} + +/** Like fascist_firewall_allows_rs, but takes pref_ipv6. */ +static int +fascist_firewall_allows_rs_impl(const routerstatus_t *rs, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + if (!rs) { + return 0; + } + + /* Assume IPv4 and IPv6 DirPorts are the same */ + return fascist_firewall_allows_base(rs->addr, rs->or_port, rs->dir_port, + &rs->ipv6_addr, rs->ipv6_orport, + rs->dir_port, fw_connection, pref_only, + pref_ipv6); } -/** Return true iff we think our firewall will let us make a directory - * connection to addr:port. */ +/** Like fascist_firewall_allows_base(), but takes rs. + * When rs is a fake_status from a dir_server_t, it can have a reachable + * address, even when the corresponding node does not. + * nodes can be missing addresses when there's no consensus (IPv4 and IPv6), + * or when there is a microdescriptor consensus, but no microdescriptors + * (microdescriptors have IPv6, the microdesc consensus does not). */ int -fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port) +fascist_firewall_allows_rs(const routerstatus_t *rs, + firewall_connection_t fw_connection, int pref_only) { - return addr_policy_permits_tor_addr(addr, port, - reachable_dir_addr_policy); + if (!rs) { + return 0; + } + + /* We don't have access to the node-specific IPv6 preference, so use the + * generic IPv6 preference instead. */ + const or_options_t *options = get_options(); + int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + + return fascist_firewall_allows_rs_impl(rs, fw_connection, pref_only, + pref_ipv6); +} + +/** Return true iff we think our firewall will let us make a connection to + * ipv6_addr:ipv6_orport based on ReachableORAddresses. + * If <b>fw_connection</b> is FIREWALL_DIR_CONNECTION, returns 0. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static int +fascist_firewall_allows_md_impl(const microdesc_t *md, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + if (!md) { + return 0; + } + + /* Can't check dirport, it doesn't have one */ + if (fw_connection == FIREWALL_DIR_CONNECTION) { + return 0; + } + + /* Also can't check IPv4, doesn't have that either */ + return fascist_firewall_allows_address_addr(&md->ipv6_addr, md->ipv6_orport, + fw_connection, pref_only, + pref_ipv6); +} + +/** Like fascist_firewall_allows_base(), but takes node, and looks up pref_ipv6 + * from node_ipv6_or/dir_preferred(). */ +int +fascist_firewall_allows_node(const node_t *node, + firewall_connection_t fw_connection, + int pref_only) +{ + if (!node) { + return 0; + } + + node_assert_ok(node); + + const int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? node_ipv6_or_preferred(node) + : node_ipv6_dir_preferred(node)); + + /* Sometimes, the rs is missing the IPv6 address info, and we need to go + * all the way to the md */ + if (node->ri && fascist_firewall_allows_ri_impl(node->ri, fw_connection, + pref_only, pref_ipv6)) { + return 1; + } else if (node->rs && fascist_firewall_allows_rs_impl(node->rs, + fw_connection, + pref_only, + pref_ipv6)) { + return 1; + } else if (node->md && fascist_firewall_allows_md_impl(node->md, + fw_connection, + pref_only, + pref_ipv6)) { + return 1; + } else { + /* If we know nothing, assume it's unreachable, we'll never get an address + * to connect to. */ + return 0; + } +} + +/** Like fascist_firewall_allows_rs(), but takes ds. */ +int +fascist_firewall_allows_dir_server(const dir_server_t *ds, + firewall_connection_t fw_connection, + int pref_only) +{ + if (!ds) { + return 0; + } + + /* A dir_server_t always has a fake_status. As long as it has the same + * addresses/ports in both fake_status and dir_server_t, this works fine. + * (See #17867.) + * fascist_firewall_allows_rs only checks the addresses in fake_status. */ + return fascist_firewall_allows_rs(&ds->fake_status, fw_connection, + pref_only); +} + +/** If a and b are both valid and allowed by fw_connection, + * choose one based on want_a and return it. + * Otherwise, return whichever is allowed. + * Otherwise, return NULL. + * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). + */ +static const tor_addr_port_t * +fascist_firewall_choose_address_impl(const tor_addr_port_t *a, + const tor_addr_port_t *b, + int want_a, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + const tor_addr_port_t *use_a = NULL; + const tor_addr_port_t *use_b = NULL; + + if (fascist_firewall_allows_address_ap(a, fw_connection, pref_only, + pref_ipv6)) { + use_a = a; + } + + if (fascist_firewall_allows_address_ap(b, fw_connection, pref_only, + pref_ipv6)) { + use_b = b; + } + + /* If both are allowed */ + if (use_a && use_b) { + /* Choose a if we want it */ + return (want_a ? use_a : use_b); + } else { + /* Choose a if we have it */ + return (use_a ? use_a : use_b); + } +} + +/** If a and b are both valid and preferred by fw_connection, + * choose one based on want_a and return it. + * Otherwise, return whichever is preferred. + * If neither are preferred, and pref_only is false: + * - If a and b are both allowed by fw_connection, + * choose one based on want_a and return it. + * - Otherwise, return whichever is preferred. + * Otherwise, return NULL. */ +STATIC const tor_addr_port_t * +fascist_firewall_choose_address(const tor_addr_port_t *a, + const tor_addr_port_t *b, + int want_a, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6) +{ + const tor_addr_port_t *pref = fascist_firewall_choose_address_impl( + a, b, want_a, + fw_connection, + 1, pref_ipv6); + if (pref_only || pref) { + /* If there is a preferred address, use it. If we can only use preferred + * addresses, and neither address is preferred, pref will be NULL, and we + * want to return NULL, so return it. */ + return pref; + } else { + /* If there's no preferred address, and we can return addresses that are + * not preferred, use an address that's allowed */ + return fascist_firewall_choose_address_impl(a, b, want_a, fw_connection, + 0, pref_ipv6); + } +} + +/** Copy an address and port into <b>ap</b> that we think our firewall will + * let us connect to. Uses ipv4_addr/ipv6_addr and + * ipv4_orport/ipv6_orport/ReachableORAddresses or + * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and + * <b>fw_connection</b>. + * If pref_only, only choose preferred addresses. In either case, choose + * a preferred address before an address that's not preferred. + * If both addresses could be chosen (they are both preferred or both allowed) + * choose IPv6 if pref_ipv6 is true, otherwise choose IPv4. + * If neither address is chosen, return 0, else return 1. */ +static int +fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, + uint16_t ipv4_orport, + uint16_t ipv4_dirport, + const tor_addr_t *ipv6_addr, + uint16_t ipv6_orport, + uint16_t ipv6_dirport, + firewall_connection_t fw_connection, + int pref_only, + int pref_ipv6, + tor_addr_port_t* ap) +{ + const tor_addr_port_t *result = NULL; + const int want_ipv4 = !pref_ipv6; + + tor_assert(ipv6_addr); + tor_assert(ap); + + tor_addr_port_t ipv4_ap; + tor_addr_copy(&ipv4_ap.addr, ipv4_addr); + ipv4_ap.port = (fw_connection == FIREWALL_OR_CONNECTION + ? ipv4_orport + : ipv4_dirport); + + tor_addr_port_t ipv6_ap; + tor_addr_copy(&ipv6_ap.addr, ipv6_addr); + ipv6_ap.port = (fw_connection == FIREWALL_OR_CONNECTION + ? ipv6_orport + : ipv6_dirport); + + result = fascist_firewall_choose_address(&ipv4_ap, &ipv6_ap, + want_ipv4, + fw_connection, pref_only, + pref_ipv6); + + if (result) { + tor_addr_copy(&ap->addr, &result->addr); + ap->port = result->port; + return 1; + } else { + return 0; + } +} + +/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4 + * address as the first parameter. */ +static int +fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, + uint16_t ipv4_orport, + uint16_t ipv4_dirport, + const tor_addr_t *ipv6_addr, + uint16_t ipv6_orport, + uint16_t ipv6_dirport, + firewall_connection_t fw_connection, + int pref_only, + int pref_ipv6, + tor_addr_port_t* ap) +{ + tor_addr_t ipv4_addr; + tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr); + return fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport, + ipv4_dirport, ipv6_addr, + ipv6_orport, ipv6_dirport, + fw_connection, pref_only, + pref_ipv6, ap); +} + +/** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. + * Consults the corresponding node, then falls back to rs if node is NULL. + * This should only happen when there's no valid consensus, and rs doesn't + * correspond to a bridge client's bridge. + */ +int +fascist_firewall_choose_address_rs(const routerstatus_t *rs, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap) +{ + if (!rs) { + return 0; + } + + tor_assert(ap); + + const node_t *node = node_get_by_id(rs->identity_digest); + + if (node) { + return fascist_firewall_choose_address_node(node, fw_connection, pref_only, + ap); + } else { + /* There's no node-specific IPv6 preference, so use the generic IPv6 + * preference instead. */ + const or_options_t *options = get_options(); + int pref_ipv6 = (fw_connection == FIREWALL_OR_CONNECTION + ? fascist_firewall_prefer_ipv6_orport(options) + : fascist_firewall_prefer_ipv6_dirport(options)); + + /* Assume IPv4 and IPv6 DirPorts are the same. + * Assume the IPv6 OR and Dir addresses are the same. */ + return fascist_firewall_choose_address_ipv4h(rs->addr, + rs->or_port, + rs->dir_port, + &rs->ipv6_addr, + rs->ipv6_orport, + rs->dir_port, + fw_connection, + pref_only, + pref_ipv6, + ap); + } +} + +/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and + * looks up the node's IPv6 preference rather than taking an argument + * for pref_ipv6. */ +int +fascist_firewall_choose_address_node(const node_t *node, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t *ap) +{ + if (!node) { + return 0; + } + + node_assert_ok(node); + + const int pref_ipv6_node = (fw_connection == FIREWALL_OR_CONNECTION + ? node_ipv6_or_preferred(node) + : node_ipv6_dir_preferred(node)); + + tor_addr_port_t ipv4_or_ap; + node_get_prim_orport(node, &ipv4_or_ap); + tor_addr_port_t ipv4_dir_ap; + node_get_prim_dirport(node, &ipv4_dir_ap); + + tor_addr_port_t ipv6_or_ap; + node_get_pref_ipv6_orport(node, &ipv6_or_ap); + tor_addr_port_t ipv6_dir_ap; + node_get_pref_ipv6_dirport(node, &ipv6_dir_ap); + + /* Assume the IPv6 OR and Dir addresses are the same. */ + return fascist_firewall_choose_address_base(&ipv4_or_ap.addr, + ipv4_or_ap.port, + ipv4_dir_ap.port, + &ipv6_or_ap.addr, + ipv6_or_ap.port, + ipv6_dir_ap.port, + fw_connection, + pref_only, + pref_ipv6_node, + ap); +} + +/** Like fascist_firewall_choose_address_rs(), but takes <b>ds</b>. */ +int +fascist_firewall_choose_address_dir_server(const dir_server_t *ds, + firewall_connection_t fw_connection, + int pref_only, + tor_addr_port_t *ap) +{ + if (!ds) { + return 0; + } + + /* A dir_server_t always has a fake_status. As long as it has the same + * addresses/ports in both fake_status and dir_server_t, this works fine. + * (See #17867.) + * This function relies on fascist_firewall_choose_address_rs looking up the + * node if it can, because that will get the latest info for the relay. */ + return fascist_firewall_choose_address_rs(&ds->fake_status, fw_connection, + pref_only, ap); } /** Return 1 if <b>addr</b> is permitted to connect to our dir port, @@ -430,7 +1076,7 @@ validate_addr_policies(const or_options_t *options, char **msg) smartlist_t *addr_policy=NULL; *msg = NULL; - if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) { + if (policies_parse_exit_policy_from_options(options,0,NULL,&addr_policy)) { REJECT("Error in ExitPolicy entry."); } @@ -568,6 +1214,8 @@ cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b) return r; if ((r=((int)a->is_private - (int)b->is_private))) return r; + /* refcnt and is_canonical are irrelevant to equality, + * they are hash table implementation details */ if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT))) return r; if ((r=((int)a->maskbits - (int)b->maskbits))) @@ -610,7 +1258,7 @@ typedef struct policy_map_ent_t { static HT_HEAD(policy_map, policy_map_ent_t) policy_root = HT_INITIALIZER(); /** Return true iff a and b are equal. */ -static INLINE int +static inline int policy_eq(policy_map_ent_t *a, policy_map_ent_t *b) { return cmp_single_addr_policy(a->policy, b->policy) == 0; @@ -678,6 +1326,10 @@ compare_known_tor_addr_to_addr_policy(const tor_addr_t *addr, uint16_t port, /* We know the address and port, and we know the policy, so we can just * compute an exact match. */ SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) { + if (tmpe->addr.family == AF_UNSPEC) { + log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only " + "matches other AF_UNSPEC addresses."); + } /* Address is known */ if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits, CMP_EXACT)) { @@ -705,6 +1357,10 @@ compare_known_tor_addr_to_addr_policy_noport(const tor_addr_t *addr, int maybe_accept = 0, maybe_reject = 0; SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) { + if (tmpe->addr.family == AF_UNSPEC) { + log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only " + "matches other AF_UNSPEC addresses."); + } if (!tor_addr_compare_masked(addr, &tmpe->addr, tmpe->maskbits, CMP_EXACT)) { if (tmpe->prt_min <= 1 && tmpe->prt_max >= 65535) { @@ -744,6 +1400,10 @@ compare_unknown_tor_addr_to_addr_policy(uint16_t port, int maybe_accept = 0, maybe_reject = 0; SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, tmpe) { + if (tmpe->addr.family == AF_UNSPEC) { + log_warn(LD_BUG, "Policy contains an AF_UNSPEC address, which only " + "matches other AF_UNSPEC addresses."); + } if (tmpe->prt_min <= port && port <= tmpe->prt_max) { if (tmpe->maskbits == 0) { /* Definitely matches, since it covers all addresses. */ @@ -849,7 +1509,7 @@ addr_policy_intersects(addr_policy_t *a, addr_policy_t *b) /** Add the exit policy described by <b>more</b> to <b>policy</b>. */ -static void +STATIC void append_exit_policy_string(smartlist_t **policy, const char *more) { config_line_t tmp; @@ -866,6 +1526,9 @@ append_exit_policy_string(smartlist_t **policy, const char *more) void addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr) { + tor_assert(dest); + tor_assert(addr); + addr_policy_t p, *add; memset(&p, 0, sizeof(p)); p.policy_type = ADDR_POLICY_REJECT; @@ -878,6 +1541,71 @@ addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr) if (!*dest) *dest = smartlist_new(); smartlist_add(*dest, add); + log_debug(LD_CONFIG, "Adding a reject ExitPolicy 'reject %s:*'", + fmt_addr(addr)); +} + +/* Is addr public for the purposes of rejection? */ +static int +tor_addr_is_public_for_reject(const tor_addr_t *addr) +{ + return (!tor_addr_is_null(addr) && !tor_addr_is_internal(addr, 0) + && !tor_addr_is_multicast(addr)); +} + +/* Add "reject <b>addr</b>:*" to <b>dest</b>, creating the list as needed. + * Filter the address, only adding an IPv4 reject rule if ipv4_rules + * is true, and similarly for ipv6_rules. Check each address returns true for + * tor_addr_is_public_for_reject before adding it. + */ +static void +addr_policy_append_reject_addr_filter(smartlist_t **dest, + const tor_addr_t *addr, + int ipv4_rules, + int ipv6_rules) +{ + tor_assert(dest); + tor_assert(addr); + + /* Only reject IP addresses which are public */ + if (tor_addr_is_public_for_reject(addr)) { + + /* Reject IPv4 addresses and IPv6 addresses based on the filters */ + int is_ipv4 = tor_addr_is_v4(addr); + if ((is_ipv4 && ipv4_rules) || (!is_ipv4 && ipv6_rules)) { + addr_policy_append_reject_addr(dest, addr); + } + } +} + +/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the + * list as needed. */ +void +addr_policy_append_reject_addr_list(smartlist_t **dest, + const smartlist_t *addrs) +{ + tor_assert(dest); + tor_assert(addrs); + + SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) { + addr_policy_append_reject_addr(dest, addr); + } SMARTLIST_FOREACH_END(addr); +} + +/** Add "reject addr:*" to <b>dest</b>, for each addr in addrs, creating the + * list as needed. Filter using */ +static void +addr_policy_append_reject_addr_list_filter(smartlist_t **dest, + const smartlist_t *addrs, + int ipv4_rules, + int ipv6_rules) +{ + tor_assert(dest); + tor_assert(addrs); + + SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, addr) { + addr_policy_append_reject_addr_filter(dest, addr, ipv4_rules, ipv6_rules); + } SMARTLIST_FOREACH_END(addr); } /** Detect and excise "dead code" from the policy *<b>dest</b>. */ @@ -964,42 +1692,197 @@ exit_policy_remove_redundancies(smartlist_t *dest) } } +/** Reject private helper for policies_parse_exit_policy_internal: rejects + * publicly routable addresses on this exit relay. + * + * Add reject entries to the linked list *<b>dest</b>: + * <ul> + * <li>if configured_addresses is non-NULL, add entries that reject each + * tor_addr_t in the list as a destination. + * <li>if reject_interface_addresses is true, add entries that reject each + * public IPv4 and IPv6 address of each interface on this machine. + * <li>if reject_configured_port_addresses is true, add entries that reject + * each IPv4 and IPv6 address configured for a port. + * </ul> + * + * IPv6 entries are only added if ipv6_exit is true. (All IPv6 addresses are + * already blocked by policies_parse_exit_policy_internal if ipv6_exit is + * false.) + * + * The list in <b>dest</b> is created as needed. + */ +void +policies_parse_exit_policy_reject_private( + smartlist_t **dest, + int ipv6_exit, + const smartlist_t *configured_addresses, + int reject_interface_addresses, + int reject_configured_port_addresses) +{ + tor_assert(dest); + + /* Reject configured addresses, if they are from public netblocks. */ + if (configured_addresses) { + addr_policy_append_reject_addr_list_filter(dest, configured_addresses, + 1, ipv6_exit); + } + + /* Reject configured port addresses, if they are from public netblocks. */ + if (reject_configured_port_addresses) { + const smartlist_t *port_addrs = get_configured_ports(); + + SMARTLIST_FOREACH_BEGIN(port_addrs, port_cfg_t *, port) { + + /* Only reject port IP addresses, not port unix sockets */ + if (!port->is_unix_addr) { + addr_policy_append_reject_addr_filter(dest, &port->addr, 1, ipv6_exit); + } + } SMARTLIST_FOREACH_END(port); + } + + /* Reject local addresses from public netblocks on any interface. */ + if (reject_interface_addresses) { + smartlist_t *public_addresses = NULL; + + /* Reject public IPv4 addresses on any interface */ + public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0); + addr_policy_append_reject_addr_list_filter(dest, public_addresses, 1, 0); + free_interface_address6_list(public_addresses); + + /* Don't look for IPv6 addresses if we're configured as IPv4-only */ + if (ipv6_exit) { + /* Reject public IPv6 addresses on any interface */ + public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0); + addr_policy_append_reject_addr_list_filter(dest, public_addresses, 0, 1); + free_interface_address6_list(public_addresses); + } + } + + /* If addresses were added multiple times, remove all but one of them. */ + if (*dest) { + exit_policy_remove_redundancies(*dest); + } +} + +/** + * Iterate through <b>policy</b> looking for redundant entries. Log a + * warning message with the first redundant entry, if any is found. + */ +static void +policies_log_first_redundant_entry(const smartlist_t *policy) +{ + int found_final_effective_entry = 0; + int first_redundant_entry = 0; + tor_assert(policy); + SMARTLIST_FOREACH_BEGIN(policy, const addr_policy_t *, p) { + sa_family_t family; + int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0; + const int i = p_sl_idx; + + /* Look for accept/reject *[4|6|]:* entires */ + if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) { + family = tor_addr_family(&p->addr); + /* accept/reject *:* may have already been expanded into + * accept/reject *4:*,accept/reject *6:* + * But handle both forms. + */ + if (family == AF_INET || family == AF_UNSPEC) { + found_ipv4_wildcard = 1; + } + if (family == AF_INET6 || family == AF_UNSPEC) { + found_ipv6_wildcard = 1; + } + } + + /* We also find accept *4:*,reject *6:* ; and + * accept *4:*,<other policies>,accept *6:* ; and similar. + * That's ok, because they make any subsequent entries redundant. */ + if (found_ipv4_wildcard && found_ipv6_wildcard) { + found_final_effective_entry = 1; + /* if we're not on the final entry in the list */ + if (i < smartlist_len(policy) - 1) { + first_redundant_entry = i + 1; + } + break; + } + } SMARTLIST_FOREACH_END(p); + + /* Work out if there are redundant trailing entries in the policy list */ + if (found_final_effective_entry && first_redundant_entry > 0) { + const addr_policy_t *p; + /* Longest possible policy is + * "accept6 ffff:ffff:..255/128:10000-65535", + * which contains a max-length IPv6 address, plus 24 characters. */ + char line[TOR_ADDR_BUF_LEN + 32]; + + tor_assert(first_redundant_entry < smartlist_len(policy)); + p = smartlist_get(policy, first_redundant_entry); + /* since we've already parsed the policy into an addr_policy_t struct, + * we might not log exactly what the user typed in */ + policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0); + log_warn(LD_DIR, "Exit policy '%s' and all following policies are " + "redundant, as it follows accept/reject *:* rules for both " + "IPv4 and IPv6. They will be removed from the exit policy. (Use " + "accept/reject *:* as the last entry in any exit policy.)", + line); + } +} + #define DEFAULT_EXIT_POLICY \ "reject *:25,reject *:119,reject *:135-139,reject *:445," \ "reject *:563,reject *:1214,reject *:4661-4666," \ "reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*" -/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If - * cfg doesn't end in an absolute accept or reject and if +/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. + * + * If <b>ipv6_exit</b> is false, prepend "reject *6:*" to the policy. + * + * If <b>rejectprivate</b> is true: + * - prepend "reject private:*" to the policy. + * - prepend entries that reject publicly routable addresses on this exit + * relay by calling policies_parse_exit_policy_reject_private + * + * If cfg doesn't end in an absolute accept or reject and if * <b>add_default_policy</b> is true, add the default exit - * policy afterwards. If <b>rejectprivate</b> is true, prepend - * "reject private:*" to the policy. Return -1 if we can't parse cfg, - * else return 0. + * policy afterwards. + * + * Return -1 if we can't parse cfg, else return 0. * * This function is used to parse the exit policy from our torrc. For * the functions used to parse the exit policy from a router descriptor, * see router_add_exit_policy. */ static int -policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest, +policies_parse_exit_policy_internal(config_line_t *cfg, + smartlist_t **dest, int ipv6_exit, int rejectprivate, - uint32_t local_address, + const smartlist_t *configured_addresses, + int reject_interface_addresses, + int reject_configured_port_addresses, int add_default_policy) { if (!ipv6_exit) { append_exit_policy_string(dest, "reject *6:*"); } if (rejectprivate) { + /* Reject IPv4 and IPv6 reserved private netblocks */ append_exit_policy_string(dest, "reject private:*"); - if (local_address) { - char buf[POLICY_BUF_LEN]; - tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address)); - append_exit_policy_string(dest, buf); - } + /* Reject IPv4 and IPv6 publicly routable addresses on this exit relay */ + policies_parse_exit_policy_reject_private( + dest, ipv6_exit, + configured_addresses, + reject_interface_addresses, + reject_configured_port_addresses); } if (parse_addr_policy(cfg, dest, -1)) return -1; + + /* Before we add the default policy and final rejects, check to see if + * there are any lines after accept *:* or reject *:*. These lines have no + * effect, and are most likely an error. */ + policies_log_first_redundant_entry(*dest); + if (add_default_policy) { append_exit_policy_string(dest, DEFAULT_EXIT_POLICY); } else { @@ -1013,20 +1896,22 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest, /** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist. * - * Add entry that rejects all IPv6 destinations unless + * Prepend an entry that rejects all IPv6 destinations unless * <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask. * - * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>, - * do add entry that rejects all destinations in private subnetwork - * Tor is running in. + * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>: + * - prepend an entry that rejects all destinations in all netblocks + * reserved for private use. + * - prepend entries that reject publicly routable addresses on this exit + * relay by calling policies_parse_exit_policy_internal * - * Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add + * If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append * default exit policy entries to <b>result</b> smartlist. */ int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, exit_policy_parser_cfg_t options, - uint32_t local_address) + const smartlist_t *configured_addresses) { int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0; int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0; @@ -1034,20 +1919,80 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled, reject_private, - local_address, + configured_addresses, + reject_private, + reject_private, add_default); } +/** Helper function that adds a copy of addr to a smartlist as long as it is + * non-NULL and not tor_addr_is_null(). + * + * The caller is responsible for freeing all the tor_addr_t* in the smartlist. + */ +static void +policies_copy_addr_to_smartlist(smartlist_t *addr_list, const tor_addr_t *addr) +{ + if (addr && !tor_addr_is_null(addr)) { + tor_addr_t *addr_copy = tor_malloc(sizeof(tor_addr_t)); + tor_addr_copy(addr_copy, addr); + smartlist_add(addr_list, addr_copy); + } +} + +/** Helper function that adds ipv4h_addr to a smartlist as a tor_addr_t *, + * as long as it is not tor_addr_is_null(), by converting it to a tor_addr_t + * and passing it to policies_add_addr_to_smartlist. + * + * The caller is responsible for freeing all the tor_addr_t* in the smartlist. + */ +static void +policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr) +{ + if (ipv4h_addr) { + tor_addr_t ipv4_tor_addr; + tor_addr_from_ipv4h(&ipv4_tor_addr, ipv4h_addr); + policies_copy_addr_to_smartlist(addr_list, &ipv4_tor_addr); + } +} + +/** Helper function that adds copies of + * or_options->OutboundBindAddressIPv[4|6]_ to a smartlist as tor_addr_t *, as + * long as or_options is non-NULL, and the addresses are not + * tor_addr_is_null(), by passing them to policies_add_addr_to_smartlist. + * + * The caller is responsible for freeing all the tor_addr_t* in the smartlist. + */ +static void +policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list, + const or_options_t *or_options) +{ + if (or_options) { + policies_copy_addr_to_smartlist(addr_list, + &or_options->OutboundBindAddressIPv4_); + policies_copy_addr_to_smartlist(addr_list, + &or_options->OutboundBindAddressIPv6_); + } +} + /** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b> * smartlist. - * If <b>or_options->IPv6Exit</b> is false, add an entry that + * If <b>or_options->IPv6Exit</b> is false, prepend an entry that * rejects all IPv6 destinations. * - * If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that - * rejects all destinations in the private subnetwork of machine Tor - * instance is running in. + * If <b>or_options->ExitPolicyRejectPrivate</b> is true: + * - prepend an entry that rejects all destinations in all netblocks reserved + * for private use. + * - if local_address is non-zero, treat it as a host-order IPv4 address, and + * add it to the list of configured addresses. + * - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it + * to the list of configured addresses. + * - if or_options->OutboundBindAddressIPv4_ is not the null tor_addr_t, add + * it to the list of configured addresses. + * - if or_options->OutboundBindAddressIPv6_ is not the null tor_addr_t, add + * it to the list of configured addresses. * - * If <b>or_options->BridgeRelay</b> is false, add entries of default + * If <b>or_options->BridgeRelay</b> is false, append entries of default * Tor exit policy into <b>result</b> smartlist. * * If or_options->ExitRelay is false, then make our exit policy into @@ -1056,16 +2001,23 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, int policies_parse_exit_policy_from_options(const or_options_t *or_options, uint32_t local_address, + const tor_addr_t *ipv6_local_address, smartlist_t **result) { exit_policy_parser_cfg_t parser_cfg = 0; + smartlist_t *configured_addresses = NULL; + int rv = 0; + /* Short-circuit for non-exit relays */ if (or_options->ExitRelay == 0) { append_exit_policy_string(result, "reject *4:*"); append_exit_policy_string(result, "reject *6:*"); return 0; } + configured_addresses = smartlist_new(); + + /* Configure the parser */ if (or_options->IPv6Exit) { parser_cfg |= EXIT_POLICY_IPV6_ENABLED; } @@ -1078,8 +2030,19 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, parser_cfg |= EXIT_POLICY_ADD_DEFAULT; } - return policies_parse_exit_policy(or_options->ExitPolicy,result, - parser_cfg,local_address); + /* Copy the configured addresses into the tor_addr_t* list */ + policies_copy_ipv4h_to_smartlist(configured_addresses, local_address); + policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address); + policies_copy_outbound_addresses_to_smartlist(configured_addresses, + or_options); + + rv = policies_parse_exit_policy(or_options->ExitPolicy, result, parser_cfg, + configured_addresses); + + SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a)); + smartlist_free(configured_addresses); + + return rv; } /** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating @@ -1187,7 +2150,7 @@ policy_is_reject_star(const smartlist_t *policy, sa_family_t family) /** Write a single address policy to the buf_len byte buffer at buf. Return * the number of characters written, or -1 on failure. */ int -policy_write_item(char *buf, size_t buflen, addr_policy_t *policy, +policy_write_item(char *buf, size_t buflen, const addr_policy_t *policy, int format_for_desc) { size_t written = 0; @@ -1223,9 +2186,9 @@ policy_write_item(char *buf, size_t buflen, addr_policy_t *policy, if (result < 0) return -1; written += strlen(buf); - /* If the maskbits is 32 we don't need to give it. If the mask is 0, - * we already wrote "*". */ - if (policy->maskbits < 32 && policy->maskbits > 0) { + /* If the maskbits is 32 (IPv4) or 128 (IPv6) we don't need to give it. If + the mask is 0, we already wrote "*". */ + if (policy->maskbits < (is_ip6?128:32) && policy->maskbits > 0) { if (tor_snprintf(buf+written, buflen-written, "/%d", policy->maskbits)<0) return -1; written += strlen(buf+written); @@ -1705,7 +2668,7 @@ compare_tor_addr_to_short_policy(const tor_addr_t *addr, uint16_t port, * allows exit enclaving. Trying it anyway would open up a cool attack * where the node refuses due to exitpolicy, the client reacts in * surprise by rewriting the node's exitpolicy to reject *:*, and then - * a bad guy targets users by causing them to attempt such connections + * an adversary targets users by causing them to attempt such connections * to 98% of the exits. * * Once microdescriptors can handle addresses in special cases (e.g. if @@ -1766,6 +2729,53 @@ compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port, } } +/** + * Given <b>policy_list</b>, a list of addr_policy_t, produce a string + * representation of the list. + * If <b>include_ipv4</b> is true, include IPv4 entries. + * If <b>include_ipv6</b> is true, include IPv6 entries. + */ +char * +policy_dump_to_string(const smartlist_t *policy_list, + int include_ipv4, + int include_ipv6) +{ + smartlist_t *policy_string_list; + char *policy_string = NULL; + + policy_string_list = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(policy_list, addr_policy_t *, tmpe) { + char *pbuf; + int bytes_written_to_pbuf; + if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) { + continue; /* Don't include IPv6 parts of address policy */ + } + if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) { + continue; /* Don't include IPv4 parts of address policy */ + } + + pbuf = tor_malloc(POLICY_BUF_LEN); + bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1); + + if (bytes_written_to_pbuf < 0) { + log_warn(LD_BUG, "policy_dump_to_string ran out of room!"); + tor_free(pbuf); + goto done; + } + + smartlist_add(policy_string_list,pbuf); + } SMARTLIST_FOREACH_END(tmpe); + + policy_string = smartlist_join_strings(policy_string_list, "\n", 0, NULL); + + done: + SMARTLIST_FOREACH(policy_string_list, char *, str, tor_free(str)); + smartlist_free(policy_string_list); + + return policy_string; +} + /** Implementation for GETINFO control command: knows the answer for questions * about "exit-policy/..." */ int @@ -1777,6 +2787,57 @@ getinfo_helper_policies(control_connection_t *conn, (void) errmsg; if (!strcmp(question, "exit-policy/default")) { *answer = tor_strdup(DEFAULT_EXIT_POLICY); + } else if (!strcmp(question, "exit-policy/reject-private/default")) { + smartlist_t *private_policy_strings; + const char **priv = private_nets; + + private_policy_strings = smartlist_new(); + + while (*priv != NULL) { + /* IPv6 addresses are in "[]" and contain ":", + * IPv4 addresses are not in "[]" and contain "." */ + smartlist_add_asprintf(private_policy_strings, "reject %s:*", *priv); + priv++; + } + + *answer = smartlist_join_strings(private_policy_strings, + ",", 0, NULL); + + SMARTLIST_FOREACH(private_policy_strings, char *, str, tor_free(str)); + smartlist_free(private_policy_strings); + } else if (!strcmp(question, "exit-policy/reject-private/relay")) { + const or_options_t *options = get_options(); + const routerinfo_t *me = router_get_my_routerinfo(); + + if (!me) { + *errmsg = "router_get_my_routerinfo returned NULL"; + return -1; + } + + if (!options->ExitPolicyRejectPrivate) { + *answer = tor_strdup(""); + return 0; + } + + smartlist_t *private_policy_list = smartlist_new(); + smartlist_t *configured_addresses = smartlist_new(); + + /* Copy the configured addresses into the tor_addr_t* list */ + policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr); + policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr); + policies_copy_outbound_addresses_to_smartlist(configured_addresses, + options); + + policies_parse_exit_policy_reject_private( + &private_policy_list, + options->IPv6Exit, + configured_addresses, + 1, 1); + *answer = policy_dump_to_string(private_policy_list, 1, 1); + + addr_policy_list_free(private_policy_list); + SMARTLIST_FOREACH(configured_addresses, tor_addr_t *, a, tor_free(a)); + smartlist_free(configured_addresses); } else if (!strcmpstart(question, "exit-policy/")) { const routerinfo_t *me = router_get_my_routerinfo(); diff --git a/src/or/policies.h b/src/or/policies.h index 0225b57a2c..aaa6fa0a4e 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -22,13 +22,44 @@ #define EXIT_POLICY_REJECT_PRIVATE (1 << 1) #define EXIT_POLICY_ADD_DEFAULT (1 << 2) +typedef enum firewall_connection_t { + FIREWALL_OR_CONNECTION = 0, + FIREWALL_DIR_CONNECTION = 1 +} firewall_connection_t; + typedef int exit_policy_parser_cfg_t; int firewall_is_fascist_or(void); -int fascist_firewall_allows_address_or(const tor_addr_t *addr, uint16_t port); -int fascist_firewall_allows_or(const routerinfo_t *ri); -int fascist_firewall_allows_node(const node_t *node); -int fascist_firewall_allows_address_dir(const tor_addr_t *addr, uint16_t port); +int firewall_is_fascist_dir(void); +int fascist_firewall_use_ipv6(const or_options_t *options); +int fascist_firewall_prefer_ipv6_orport(const or_options_t *options); +int fascist_firewall_prefer_ipv6_dirport(const or_options_t *options); + +int fascist_firewall_allows_address_addr(const tor_addr_t *addr, + uint16_t port, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6); + +int fascist_firewall_allows_rs(const routerstatus_t *rs, + firewall_connection_t fw_connection, + int pref_only); +int fascist_firewall_allows_node(const node_t *node, + firewall_connection_t fw_connection, + int pref_only); +int fascist_firewall_allows_dir_server(const dir_server_t *ds, + firewall_connection_t fw_connection, + int pref_only); + +int fascist_firewall_choose_address_rs(const routerstatus_t *rs, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap); +int fascist_firewall_choose_address_node(const node_t *node, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap); +int fascist_firewall_choose_address_dir_server(const dir_server_t *ds, + firewall_connection_t fw_connection, + int pref_only, tor_addr_port_t* ap); + int dir_policy_permits_address(const tor_addr_t *addr); int socks_policy_permits_address(const tor_addr_t *addr); int authdir_policy_permits_address(uint32_t addr, uint16_t port); @@ -44,32 +75,38 @@ addr_policy_t *addr_policy_get_canonical_entry(addr_policy_t *ent); int cmp_addr_policies(smartlist_t *a, smartlist_t *b); MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy, (const tor_addr_t *addr, uint16_t port, const smartlist_t *policy)); - addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr, uint16_t port, const node_t *node); -/* -int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, - int ipv6exit, - int rejectprivate, uint32_t local_address, - int add_default_policy); -*/ -int policies_parse_exit_policy_from_options(const or_options_t *or_options, - uint32_t local_address, - smartlist_t **result); +int policies_parse_exit_policy_from_options( + const or_options_t *or_options, + uint32_t local_address, + const tor_addr_t *ipv6_local_address, + smartlist_t **result); int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest, exit_policy_parser_cfg_t options, - uint32_t local_address); + const smartlist_t *configured_addresses); +void policies_parse_exit_policy_reject_private( + smartlist_t **dest, + int ipv6_exit, + const smartlist_t *configured_addresses, + int reject_interface_addresses, + int reject_configured_port_addresses); void policies_exit_policy_append_reject_star(smartlist_t **dest); void addr_policy_append_reject_addr(smartlist_t **dest, const tor_addr_t *addr); +void addr_policy_append_reject_addr_list(smartlist_t **dest, + const smartlist_t *addrs); void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter); int exit_policy_is_general_exit(smartlist_t *policy); int policy_is_reject_star(const smartlist_t *policy, sa_family_t family); +char * policy_dump_to_string(const smartlist_t *policy_list, + int include_ipv4, + int include_ipv6); int getinfo_helper_policies(control_connection_t *conn, const char *question, char **answer, const char **errmsg); -int policy_write_item(char *buf, size_t buflen, addr_policy_t *item, +int policy_write_item(char *buf, size_t buflen, const addr_policy_t *item, int format_for_desc); void addr_policy_list_free(smartlist_t *p); @@ -86,5 +123,20 @@ addr_policy_result_t compare_tor_addr_to_short_policy( const tor_addr_t *addr, uint16_t port, const short_policy_t *policy); +#ifdef POLICIES_PRIVATE +STATIC void append_exit_policy_string(smartlist_t **policy, const char *more); +STATIC int fascist_firewall_allows_address(const tor_addr_t *addr, + uint16_t port, + smartlist_t *firewall_policy, + int pref_only, int pref_ipv6); +STATIC const tor_addr_port_t * fascist_firewall_choose_address( + const tor_addr_port_t *a, + const tor_addr_port_t *b, + int want_a, + firewall_connection_t fw_connection, + int pref_only, int pref_ipv6); + +#endif + #endif diff --git a/src/or/reasons.c b/src/or/reasons.c index 23ab6041a6..36921cafcd 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/reasons.h b/src/or/reasons.h index 00a099061b..2e12c93728 100644 --- a/src/or/reasons.h +++ b/src/or/reasons.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/relay.c b/src/or/relay.c index 50eaab83c8..3f7751826c 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -32,6 +32,7 @@ #include "policies.h" #include "reasons.h" #include "relay.h" +#include "rendcache.h" #include "rendcommon.h" #include "router.h" #include "routerlist.h" @@ -147,20 +148,15 @@ relay_digest_matches(crypto_digest_t *digest, cell_t *cell) * * If <b>encrypt_mode</b> is 1 then encrypt, else decrypt. * - * Return -1 if the crypto fails, else return 0. + * Returns 0. */ static int relay_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in, int encrypt_mode) { - int r; (void)encrypt_mode; - r = crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); + crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE); - if (r) { - log_warn(LD_BUG,"Error during relay encryption"); - return -1; - } return 0; } @@ -208,8 +204,7 @@ circuit_receive_relay_cell(cell_t *cell, circuit_t *circ, return 0; } - conn = relay_lookup_conn(circ, cell, cell_direction, - layer_hint); + conn = relay_lookup_conn(circ, cell, cell_direction, layer_hint); if (cell_direction == CELL_DIRECTION_OUT) { ++stats_n_relay_cells_delivered; log_debug(LD_OR,"Sending away from origin."); @@ -833,7 +828,7 @@ connection_ap_process_end_not_open( } } } - /* check if he *ought* to have allowed it */ + /* check if the exit *ought* to have allowed it */ adjust_exit_policy_from_exitpolicy_failure(circ, conn, @@ -1304,8 +1299,12 @@ connection_edge_process_relay_cell_not_open( "Got 'connected' while not in state connect_wait. Dropping."); return 0; } + CONNECTION_AP_EXPECT_NONPENDING(entry_conn); conn->base_.state = AP_CONN_STATE_OPEN; - log_info(LD_APP,"'connected' received after %d seconds.", + log_info(LD_APP,"'connected' received for circid %u streamid %d " + "after %d seconds.", + (unsigned)circ->n_circ_id, + rh->stream_id, (int)(time(NULL) - conn->base_.timestamp_lastread)); if (connected_cell_parse(rh, cell, &addr, &ttl) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_APP, @@ -1698,7 +1697,9 @@ connection_edge_process_relay_cell(cell_t *cell, circuit_t *circ, return -END_CIRC_REASON_TORPROTOCOL; } log_info(domain, - "'connected' received, no conn attached anymore. Ignoring."); + "'connected' received on circid %u for streamid %d, " + "no conn attached anymore. Ignoring.", + (unsigned)circ->n_circ_id, rh.stream_id); return 0; case RELAY_COMMAND_SENDME: if (!rh.stream_id) { @@ -2250,7 +2251,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint) static size_t total_cells_allocated = 0; /** Release storage held by <b>cell</b>. */ -static INLINE void +static inline void packed_cell_free_unchecked(packed_cell_t *cell) { --total_cells_allocated; @@ -2294,7 +2295,7 @@ dump_cell_pool_usage(int severity) } /** Allocate a new copy of packed <b>cell</b>. */ -static INLINE packed_cell_t * +static inline packed_cell_t * packed_cell_copy(const cell_t *cell, int wide_circ_ids) { packed_cell_t *c = packed_cell_new(); @@ -2373,7 +2374,7 @@ packed_cell_mem_cost(void) return sizeof(packed_cell_t); } -/** DOCDOC */ +/* DOCDOC */ STATIC size_t cell_queues_get_total_allocation(void) { @@ -2614,6 +2615,15 @@ channel_flush_from_first_active_circuit, (channel_t *chan, int max)) } /* Circuitmux told us this was active, so it should have cells */ + if (/*BUG(*/ queue->n == 0 /*)*/) { + log_warn(LD_BUG, "Found a supposedly active circuit with no cells " + "to send. Trying to recover."); + circuitmux_set_num_cells(cmux, circ, 0); + if (! circ->marked_for_close) + circuit_mark_for_close(circ, END_CIRC_REASON_INTERNAL); + continue; + } + tor_assert(queue->n > 0); /* diff --git a/src/or/relay.h b/src/or/relay.h index a4f583d11e..e15551ca51 100644 --- a/src/or/relay.h +++ b/src/or/relay.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/rendcache.c b/src/or/rendcache.c new file mode 100644 index 0000000000..f8206cd53b --- /dev/null +++ b/src/or/rendcache.c @@ -0,0 +1,1011 @@ +/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rendcache.c + * \brief Hidden service descriptor cache. + **/ + +#define RENDCACHE_PRIVATE +#include "rendcache.h" + +#include "config.h" +#include "rephist.h" +#include "routerlist.h" +#include "routerparse.h" +#include "rendcommon.h" + +/** Map from service id (as generated by rend_get_service_id) to + * rend_cache_entry_t. */ +STATIC strmap_t *rend_cache = NULL; + +/** Map from service id to rend_cache_entry_t; only for hidden services. */ +static strmap_t *rend_cache_local_service = NULL; + +/** Map from descriptor id to rend_cache_entry_t; only for hidden service + * directories. */ +STATIC digestmap_t *rend_cache_v2_dir = NULL; + +/** (Client side only) Map from service id to rend_cache_failure_t. This + * cache is used to track intro point(IP) failures so we know when to keep + * or discard a new descriptor we just fetched. Here is a description of the + * cache behavior. + * + * Everytime tor discards an IP (ex: receives a NACK), we add an entry to + * this cache noting the identity digest of the IP and it's failure type for + * the service ID. The reason we indexed this cache by service ID is to + * differentiate errors that can occur only for a specific service like a + * NACK for instance. It applies for one but maybe not for the others. + * + * Once a service descriptor is fetched and considered valid, each IP is + * looked up in this cache and if present, it is discarded from the fetched + * descriptor. At the end, all IP(s) in the cache, for a specific service + * ID, that were NOT present in the descriptor are removed from this cache. + * Which means that if at least one IP was not in this cache, thus usuable, + * it's considered a new descriptor so we keep it. Else, if all IPs were in + * this cache, we discard the descriptor as it's considered unsuable. + * + * Once a descriptor is removed from the rend cache or expires, the entry + * in this cache is also removed for the service ID. + * + * This scheme allows us to not realy on the descriptor's timestamp (which + * is rounded down to the hour) to know if we have a newer descriptor. We + * only rely on the usability of intro points from an internal state. */ +STATIC strmap_t *rend_cache_failure = NULL; + +/* DOCDOC */ +STATIC size_t rend_cache_total_allocation = 0; + +/** Initializes the service descriptor cache. +*/ +void +rend_cache_init(void) +{ + rend_cache = strmap_new(); + rend_cache_v2_dir = digestmap_new(); + rend_cache_local_service = strmap_new(); + rend_cache_failure = strmap_new(); +} + +/** Return the approximate number of bytes needed to hold <b>e</b>. */ +STATIC size_t +rend_cache_entry_allocation(const rend_cache_entry_t *e) +{ + if (!e) + return 0; + + /* This doesn't count intro_nodes or key size */ + return sizeof(*e) + e->len + sizeof(*e->parsed); +} + +/* DOCDOC */ +size_t +rend_cache_get_total_allocation(void) +{ + return rend_cache_total_allocation; +} + +/** Decrement the total bytes attributed to the rendezvous cache by n. */ +STATIC void +rend_cache_decrement_allocation(size_t n) +{ + static int have_underflowed = 0; + + if (rend_cache_total_allocation >= n) { + rend_cache_total_allocation -= n; + } else { + rend_cache_total_allocation = 0; + if (! have_underflowed) { + have_underflowed = 1; + log_warn(LD_BUG, "Underflow in rend_cache_decrement_allocation"); + } + } +} + +/** Increase the total bytes attributed to the rendezvous cache by n. */ +STATIC void +rend_cache_increment_allocation(size_t n) +{ + static int have_overflowed = 0; + if (rend_cache_total_allocation <= SIZE_MAX - n) { + rend_cache_total_allocation += n; + } else { + rend_cache_total_allocation = SIZE_MAX; + if (! have_overflowed) { + have_overflowed = 1; + log_warn(LD_BUG, "Overflow in rend_cache_increment_allocation"); + } + } +} + +/** Helper: free a rend cache failure intro object. */ +STATIC void +rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t *entry) +{ + if (entry == NULL) { + return; + } + tor_free(entry); +} + +static void +rend_cache_failure_intro_entry_free_(void *entry) +{ + rend_cache_failure_intro_entry_free(entry); +} + +/** Allocate a rend cache failure intro object and return it. <b>failure</b> + * is set into the object. This function can not fail. */ +STATIC rend_cache_failure_intro_t * +rend_cache_failure_intro_entry_new(rend_intro_point_failure_t failure) +{ + rend_cache_failure_intro_t *entry = tor_malloc(sizeof(*entry)); + entry->failure_type = failure; + entry->created_ts = time(NULL); + return entry; +} + +/** Helper: free a rend cache failure object. */ +STATIC void +rend_cache_failure_entry_free(rend_cache_failure_t *entry) +{ + if (entry == NULL) { + return; + } + + /* Free and remove every intro failure object. */ + digestmap_free(entry->intro_failures, + rend_cache_failure_intro_entry_free_); + + tor_free(entry); +} + +/** Helper: deallocate a rend_cache_failure_t. (Used with strmap_free(), + * which requires a function pointer whose argument is void*). */ +STATIC void +rend_cache_failure_entry_free_(void *entry) +{ + rend_cache_failure_entry_free(entry); +} + +/** Allocate a rend cache failure object and return it. This function can + * not fail. */ +STATIC rend_cache_failure_t * +rend_cache_failure_entry_new(void) +{ + rend_cache_failure_t *entry = tor_malloc(sizeof(*entry)); + entry->intro_failures = digestmap_new(); + return entry; +} + +/** Remove failure cache entry for the service ID in the given descriptor + * <b>desc</b>. */ +STATIC void +rend_cache_failure_remove(rend_service_descriptor_t *desc) +{ + char service_id[REND_SERVICE_ID_LEN_BASE32 + 1]; + rend_cache_failure_t *entry; + + if (desc == NULL) { + return; + } + if (rend_get_service_id(desc->pk, service_id) < 0) { + return; + } + entry = strmap_get_lc(rend_cache_failure, service_id); + if (entry != NULL) { + strmap_remove_lc(rend_cache_failure, service_id); + rend_cache_failure_entry_free(entry); + } +} + +/** Helper: free storage held by a single service descriptor cache entry. */ +STATIC void +rend_cache_entry_free(rend_cache_entry_t *e) +{ + if (!e) + return; + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + /* We are about to remove a descriptor from the cache so remove the entry + * in the failure cache. */ + rend_cache_failure_remove(e->parsed); + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + tor_free(e); +} + +/** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which + * requires a function pointer whose argument is void*). */ +static void +rend_cache_entry_free_(void *p) +{ + rend_cache_entry_free(p); +} + +/** Free all storage held by the service descriptor cache. */ +void +rend_cache_free_all(void) +{ + strmap_free(rend_cache, rend_cache_entry_free_); + digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); + strmap_free(rend_cache_local_service, rend_cache_entry_free_); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + rend_cache = NULL; + rend_cache_v2_dir = NULL; + rend_cache_local_service = NULL; + rend_cache_failure = NULL; + rend_cache_total_allocation = 0; +} + +/** Remove all entries that re REND_CACHE_FAILURE_MAX_AGE old. This is + * called every second. + * + * We have to clean these regurlarly else if for whatever reasons an hidden + * service goes offline and a client tries to connect to it during that + * time, a failure entry is created and the client will be unable to connect + * for a while even though the service has return online. */ +void +rend_cache_failure_clean(time_t now) +{ + time_t cutoff = now - REND_CACHE_FAILURE_MAX_AGE; + STRMAP_FOREACH_MODIFY(rend_cache_failure, key, + rend_cache_failure_t *, ent) { + /* Free and remove every intro failure object that match the cutoff. */ + DIGESTMAP_FOREACH_MODIFY(ent->intro_failures, ip_key, + rend_cache_failure_intro_t *, ip_ent) { + if (ip_ent->created_ts < cutoff) { + rend_cache_failure_intro_entry_free(ip_ent); + MAP_DEL_CURRENT(ip_key); + } + } DIGESTMAP_FOREACH_END; + /* If the entry is now empty of intro point failures, remove it. */ + if (digestmap_isempty(ent->intro_failures)) { + rend_cache_failure_entry_free(ent); + MAP_DEL_CURRENT(key); + } + } STRMAP_FOREACH_END; +} + +/** Removes all old entries from the client or service descriptor cache. +*/ +void +rend_cache_clean(time_t now, rend_cache_type_t cache_type) +{ + strmap_iter_t *iter; + const char *key; + void *val; + rend_cache_entry_t *ent; + time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; + strmap_t *cache = NULL; + + if (cache_type == REND_CACHE_TYPE_CLIENT) { + cache = rend_cache; + } else if (cache_type == REND_CACHE_TYPE_SERVICE) { + cache = rend_cache_local_service; + } + tor_assert(cache); + + for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) { + strmap_iter_get(iter, &key, &val); + ent = (rend_cache_entry_t*)val; + if (ent->parsed->timestamp < cutoff) { + iter = strmap_iter_next_rmv(cache, iter); + rend_cache_entry_free(ent); + } else { + iter = strmap_iter_next(cache, iter); + } + } +} + +/** Remove ALL entries from the rendezvous service descriptor cache. +*/ +void +rend_cache_purge(void) +{ + if (rend_cache) { + log_info(LD_REND, "Purging HS descriptor cache"); + strmap_free(rend_cache, rend_cache_entry_free_); + } + rend_cache = strmap_new(); +} + +/** Remove ALL entries from the failure cache. This is also called when a + * NEWNYM signal is received. */ +void +rend_cache_failure_purge(void) +{ + if (rend_cache_failure) { + log_info(LD_REND, "Purging HS failure cache"); + strmap_free(rend_cache_failure, rend_cache_failure_entry_free_); + } + rend_cache_failure = strmap_new(); +} + +/** Lookup the rend failure cache using a relay identity digest in + * <b>identity</b> which has DIGEST_LEN bytes and service ID <b>service_id</b> + * which is a null-terminated string. If found, the intro failure is set in + * <b>intro_entry</b> else it stays untouched. Return 1 iff found else 0. */ +STATIC int +cache_failure_intro_lookup(const uint8_t *identity, const char *service_id, + rend_cache_failure_intro_t **intro_entry) +{ + rend_cache_failure_t *elem; + rend_cache_failure_intro_t *intro_elem; + + tor_assert(rend_cache_failure); + + if (intro_entry) { + *intro_entry = NULL; + } + + /* Lookup descriptor and return it. */ + elem = strmap_get_lc(rend_cache_failure, service_id); + if (elem == NULL) { + goto not_found; + } + intro_elem = digestmap_get(elem->intro_failures, (char *) identity); + if (intro_elem == NULL) { + goto not_found; + } + if (intro_entry) { + *intro_entry = intro_elem; + } + return 1; + not_found: + return 0; +} + +/** Allocate a new cache failure intro object and copy the content from + * <b>entry</b> to this newly allocated object. Return it. */ +static rend_cache_failure_intro_t * +cache_failure_intro_dup(const rend_cache_failure_intro_t *entry) +{ + rend_cache_failure_intro_t *ent_dup = + rend_cache_failure_intro_entry_new(entry->failure_type); + ent_dup->created_ts = entry->created_ts; + return ent_dup; +} + +/** Add an intro point failure to the failure cache using the relay + * <b>identity</b> and service ID <b>service_id</b>. Record the + * <b>failure</b> in that object. */ +STATIC void +cache_failure_intro_add(const uint8_t *identity, const char *service_id, + rend_intro_point_failure_t failure) +{ + rend_cache_failure_t *fail_entry; + rend_cache_failure_intro_t *entry, *old_entry; + + /* Make sure we have a failure object for this service ID and if not, + * create it with this new intro failure entry. */ + fail_entry = strmap_get_lc(rend_cache_failure, service_id); + if (fail_entry == NULL) { + fail_entry = rend_cache_failure_entry_new(); + /* Add failure entry to global rend failure cache. */ + strmap_set_lc(rend_cache_failure, service_id, fail_entry); + } + entry = rend_cache_failure_intro_entry_new(failure); + old_entry = digestmap_set(fail_entry->intro_failures, + (char *) identity, entry); + /* This _should_ be NULL, but in case it isn't, free it. */ + rend_cache_failure_intro_entry_free(old_entry); +} + +/** Using a parsed descriptor <b>desc</b>, check if the introduction points + * are present in the failure cache and if so they are removed from the + * descriptor and kept into the failure cache. Then, each intro points that + * are NOT in the descriptor but in the failure cache for the given + * <b>service_id</b> are removed from the failure cache. */ +STATIC void +validate_intro_point_failure(const rend_service_descriptor_t *desc, + const char *service_id) +{ + rend_cache_failure_t *new_entry, *cur_entry; + /* New entry for the service ID that will be replacing the one in the + * failure cache since we have a new descriptor. In the case where all + * intro points are removed, we are assured that the new entry is the same + * as the current one. */ + new_entry = tor_malloc(sizeof(*new_entry)); + new_entry->intro_failures = digestmap_new(); + + tor_assert(desc); + + SMARTLIST_FOREACH_BEGIN(desc->intro_nodes, rend_intro_point_t *, intro) { + int found; + rend_cache_failure_intro_t *entry; + const uint8_t *identity = + (uint8_t *) intro->extend_info->identity_digest; + + found = cache_failure_intro_lookup(identity, service_id, &entry); + if (found) { + /* Dup here since it will be freed at the end when removing the + * original entry in the cache. */ + rend_cache_failure_intro_t *ent_dup = cache_failure_intro_dup(entry); + /* This intro point is in our cache, discard it from the descriptor + * because chances are that it's unusable. */ + SMARTLIST_DEL_CURRENT(desc->intro_nodes, intro); + /* Keep it for our new entry. */ + digestmap_set(new_entry->intro_failures, (char *) identity, ent_dup); + /* Only free it when we're done looking at it. */ + rend_intro_point_free(intro); + continue; + } + } SMARTLIST_FOREACH_END(intro); + + /* Swap the failure entry in the cache and free the current one. */ + cur_entry = strmap_get_lc(rend_cache_failure, service_id); + if (cur_entry != NULL) { + rend_cache_failure_entry_free(cur_entry); + } + strmap_set_lc(rend_cache_failure, service_id, new_entry); +} + +/** Note down an intro failure in the rend failure cache using the type of + * failure in <b>failure</b> for the relay identity digest in + * <b>identity</b> and service ID <b>service_id</b>. If an entry already + * exists in the cache, the failure type is changed with <b>failure</b>. */ +void +rend_cache_intro_failure_note(rend_intro_point_failure_t failure, + const uint8_t *identity, + const char *service_id) +{ + int found; + rend_cache_failure_intro_t *entry; + + found = cache_failure_intro_lookup(identity, service_id, &entry); + if (!found) { + cache_failure_intro_add(identity, service_id, failure); + } else { + /* Replace introduction point failure with this one. */ + entry->failure_type = failure; + } +} + +/** Remove all old v2 descriptors and those for which this hidden service + * directory is not responsible for any more. + * + * If at all possible, remove at least <b>force_remove</b> bytes of data. + */ +void +rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove) +{ + digestmap_iter_t *iter; + time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; + const int LAST_SERVED_CUTOFF_STEP = 1800; + time_t last_served_cutoff = cutoff; + size_t bytes_removed = 0; + do { + for (iter = digestmap_iter_init(rend_cache_v2_dir); + !digestmap_iter_done(iter); ) { + const char *key; + void *val; + rend_cache_entry_t *ent; + digestmap_iter_get(iter, &key, &val); + ent = val; + if (ent->parsed->timestamp < cutoff || + ent->last_served < last_served_cutoff) { + char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); + log_info(LD_REND, "Removing descriptor with ID '%s' from cache", + safe_str_client(key_base32)); + bytes_removed += rend_cache_entry_allocation(ent); + iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); + rend_cache_entry_free(ent); + } else { + iter = digestmap_iter_next(rend_cache_v2_dir, iter); + } + } + + /* In case we didn't remove enough bytes, advance the cutoff a little. */ + last_served_cutoff += LAST_SERVED_CUTOFF_STEP; + if (last_served_cutoff > now) + break; + } while (bytes_removed < force_remove); +} + +/** Lookup in the client cache the given service ID <b>query</b> for + * <b>version</b>. + * + * Return 0 if found and if <b>e</b> is non NULL, set it with the entry + * found. Else, a negative value is returned and <b>e</b> is untouched. + * -EINVAL means that <b>query</b> is not a valid service id. + * -ENOENT means that no entry in the cache was found. */ +int +rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) +{ + int ret = 0; + char key[REND_SERVICE_ID_LEN_BASE32 + 2]; /* <version><query>\0 */ + rend_cache_entry_t *entry = NULL; + static const int default_version = 2; + + tor_assert(rend_cache); + tor_assert(query); + + if (!rend_valid_service_id(query)) { + ret = -EINVAL; + goto end; + } + + switch (version) { + case 0: + log_warn(LD_REND, "Cache lookup of a v0 renddesc is deprecated."); + break; + case 2: + /* Default is version 2. */ + default: + tor_snprintf(key, sizeof(key), "%d%s", default_version, query); + entry = strmap_get_lc(rend_cache, key); + break; + } + if (!entry) { + ret = -ENOENT; + goto end; + } + tor_assert(entry->parsed && entry->parsed->intro_nodes); + + if (e) { + *e = entry; + } + + end: + return ret; +} + +/* + * Lookup the v2 service descriptor with the service ID <b>query</b> in the + * local service descriptor cache. Return 0 if found and if <b>e</b> is + * non NULL, set it with the entry found. Else, a negative value is returned + * and <b>e</b> is untouched. + * -EINVAL means that <b>query</b> is not a valid service id. + * -ENOENT means that no entry in the cache was found. */ +int +rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e) +{ + int ret = 0; + rend_cache_entry_t *entry = NULL; + + tor_assert(rend_cache_local_service); + tor_assert(query); + + if (!rend_valid_service_id(query)) { + ret = -EINVAL; + goto end; + } + + /* Lookup descriptor and return. */ + entry = strmap_get_lc(rend_cache_local_service, query); + if (!entry) { + ret = -ENOENT; + goto end; + } + + if (e) { + *e = entry; + } + + end: + return ret; +} + +/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and + * copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on + * well-formed-but-not-found, and -1 on failure. + */ +int +rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc) +{ + rend_cache_entry_t *e; + char desc_id_digest[DIGEST_LEN]; + tor_assert(rend_cache_v2_dir); + if (base32_decode(desc_id_digest, DIGEST_LEN, + desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) { + log_fn(LOG_PROTOCOL_WARN, LD_REND, + "Rejecting v2 rendezvous descriptor request -- descriptor ID " + "contains illegal characters: %s", + safe_str(desc_id)); + return -1; + } + /* Lookup descriptor and return. */ + e = digestmap_get(rend_cache_v2_dir, desc_id_digest); + if (e) { + *desc = e->desc; + e->last_served = approx_time(); + return 1; + } + return 0; +} + +/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the + * local rend cache. Don't attempt to decrypt the included list of introduction + * points (as we don't have a descriptor cookie for it). + * + * If we have a newer descriptor with the same ID, ignore this one. + * If we have an older descriptor with the same ID, replace it. + * + * Return 0 on success, or -1 if we couldn't parse any of them. + * + * We should only call this function for public (e.g. non bridge) relays. + */ +int +rend_cache_store_v2_desc_as_dir(const char *desc) +{ + const or_options_t *options = get_options(); + rend_service_descriptor_t *parsed; + char desc_id[DIGEST_LEN]; + char *intro_content; + size_t intro_size; + size_t encoded_size; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + int number_parsed = 0, number_stored = 0; + const char *current_desc = desc; + const char *next_desc; + rend_cache_entry_t *e; + time_t now = time(NULL); + tor_assert(rend_cache_v2_dir); + tor_assert(desc); + while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, current_desc, 1) >= 0) { + number_parsed++; + /* We don't care about the introduction points. */ + tor_free(intro_content); + /* For pretty log statements. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); + /* Is descriptor too old? */ + if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { + log_info(LD_REND, "Service descriptor with desc ID %s is too old.", + safe_str(desc_id_base32)); + goto skip; + } + /* Is descriptor too far in the future? */ + if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { + log_info(LD_REND, "Service descriptor with desc ID %s is too far in the " + "future.", + safe_str(desc_id_base32)); + goto skip; + } + /* Do we already have a newer descriptor? */ + e = digestmap_get(rend_cache_v2_dir, desc_id); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a newer service descriptor with the " + "same desc ID %s and version.", + safe_str(desc_id_base32)); + goto skip; + } + /* Do we already have this descriptor? */ + if (e && !strcmp(desc, e->desc)) { + log_info(LD_REND, "We already have this service descriptor with desc " + "ID %s.", safe_str(desc_id_base32)); + goto skip; + } + /* Store received descriptor. */ + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + digestmap_set(rend_cache_v2_dir, desc_id, e); + /* Treat something just uploaded as having been served a little + * while ago, so that flooding with new descriptors doesn't help + * too much. + */ + e->last_served = approx_time() - 3600; + } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->parsed = parsed; + e->desc = tor_strndup(current_desc, encoded_size); + e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); + log_info(LD_REND, "Successfully stored service descriptor with desc ID " + "'%s' and len %d.", + safe_str(desc_id_base32), (int)encoded_size); + /* Statistics: Note down this potentially new HS. */ + if (options->HiddenServiceStatistics) { + rep_hist_stored_maybe_new_hs(e->parsed->pk); + } + + number_stored++; + goto advance; + skip: + rend_service_descriptor_free(parsed); + advance: + /* advance to next descriptor, if available. */ + current_desc = next_desc; + /* check if there is a next descriptor. */ + if (!current_desc || + strcmpstart(current_desc, "rendezvous-service-descriptor ")) + break; + } + if (!number_parsed) { + log_info(LD_REND, "Could not parse any descriptor."); + return -1; + } + log_info(LD_REND, "Parsed %d and added %d descriptor%s.", + number_parsed, number_stored, number_stored != 1 ? "s" : ""); + return 0; +} + +/** Parse the v2 service descriptor in <b>desc</b> and store it to the +* local service rend cache. Don't attempt to decrypt the included list of +* introduction points. +* +* If we have a newer descriptor with the same ID, ignore this one. +* If we have an older descriptor with the same ID, replace it. +* +* Return 0 on success, or -1 if we couldn't understand the descriptor. +*/ +int +rend_cache_store_v2_desc_as_service(const char *desc) +{ + rend_service_descriptor_t *parsed = NULL; + char desc_id[DIGEST_LEN]; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; + rend_cache_entry_t *e; + int retval = -1; + tor_assert(rend_cache_local_service); + tor_assert(desc); + + /* Parse the descriptor. */ + if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc, 0) < 0) { + log_warn(LD_REND, "Could not parse descriptor."); + goto err; + } + /* Compute service ID from public key. */ + if (rend_get_service_id(parsed->pk, service_id)<0) { + log_warn(LD_REND, "Couldn't compute service ID."); + goto err; + } + + /* Do we already have a newer descriptor? Allow new descriptors with a + rounded timestamp equal to or newer than the current descriptor */ + e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service, + service_id); + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a newer service descriptor for " + "service ID %s.", safe_str_client(service_id)); + goto okay; + } + /* We don't care about the introduction points. */ + tor_free(intro_content); + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + strmap_set_lc(rend_cache_local_service, service_id, e); + } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->parsed = parsed; + e->desc = tor_malloc_zero(encoded_size + 1); + strlcpy(e->desc, desc, encoded_size + 1); + e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); + log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", + safe_str_client(service_id), (int)encoded_size); + return 0; + + okay: + retval = 0; + + err: + rend_service_descriptor_free(parsed); + tor_free(intro_content); + return retval; +} + +/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list + * of introduction points with <b>descriptor_cookie</b> (which may also be + * <b>NULL</b> if decryption is not necessary), and store the descriptor to + * the local cache under its version and service id. + * + * If we have a newer v2 descriptor with the same ID, ignore this one. + * If we have an older descriptor with the same ID, replace it. + * If the descriptor's service ID does not match + * <b>rend_query</b>-\>onion_address, reject it. + * + * If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>, + * reject it. + * + * Return 0 on success, or -1 if we rejected the descriptor. + * If entry is not NULL, set it with the cache entry pointer of the descriptor. + */ +int +rend_cache_store_v2_desc_as_client(const char *desc, + const char *desc_id_base32, + const rend_data_t *rend_query, + rend_cache_entry_t **entry) +{ + /*XXXX this seems to have a bit of duplicate code with + * rend_cache_store_v2_desc_as_dir(). Fix that. */ + /* Though having similar elements, both functions were separated on + * purpose: + * - dirs don't care about encoded/encrypted introduction points, clients + * do. + * - dirs store descriptors in a separate cache by descriptor ID, whereas + * clients store them by service ID; both caches are different data + * structures and have different access methods. + * - dirs store a descriptor only if they are responsible for its ID, + * clients do so in every way (because they have requested it before). + * - dirs can process multiple concatenated descriptors which is required + * for replication, whereas clients only accept a single descriptor. + * Thus, combining both methods would result in a lot of if statements + * which probably would not improve, but worsen code readability. -KL */ + rend_service_descriptor_t *parsed = NULL; + char desc_id[DIGEST_LEN]; + char *intro_content = NULL; + size_t intro_size; + size_t encoded_size; + const char *next_desc; + time_t now = time(NULL); + char key[REND_SERVICE_ID_LEN_BASE32+2]; + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; + char want_desc_id[DIGEST_LEN]; + rend_cache_entry_t *e; + int retval = -1; + tor_assert(rend_cache); + tor_assert(desc); + tor_assert(desc_id_base32); + memset(want_desc_id, 0, sizeof(want_desc_id)); + if (entry) { + *entry = NULL; + } + if (base32_decode(want_desc_id, sizeof(want_desc_id), + desc_id_base32, strlen(desc_id_base32)) != 0) { + log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.", + escaped_safe_str_client(desc_id_base32)); + goto err; + } + /* Parse the descriptor. */ + if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, + &intro_size, &encoded_size, + &next_desc, desc, 0) < 0) { + log_warn(LD_REND, "Could not parse descriptor."); + goto err; + } + /* Compute service ID from public key. */ + if (rend_get_service_id(parsed->pk, service_id)<0) { + log_warn(LD_REND, "Couldn't compute service ID."); + goto err; + } + if (rend_query->onion_address[0] != '\0' && + strcmp(rend_query->onion_address, service_id)) { + log_warn(LD_REND, "Received service descriptor for service ID %s; " + "expected descriptor for service ID %s.", + service_id, safe_str(rend_query->onion_address)); + goto err; + } + if (tor_memneq(desc_id, want_desc_id, DIGEST_LEN)) { + log_warn(LD_REND, "Received service descriptor for %s with incorrect " + "descriptor ID.", service_id); + goto err; + } + + /* Decode/decrypt introduction points. */ + if (intro_content && intro_size > 0) { + int n_intro_points; + if (rend_query->auth_type != REND_NO_AUTH && + !tor_mem_is_zero(rend_query->descriptor_cookie, + sizeof(rend_query->descriptor_cookie))) { + char *ipos_decrypted = NULL; + size_t ipos_decrypted_size; + if (rend_decrypt_introduction_points(&ipos_decrypted, + &ipos_decrypted_size, + rend_query->descriptor_cookie, + intro_content, + intro_size) < 0) { + log_warn(LD_REND, "Failed to decrypt introduction points. We are " + "probably unable to parse the encoded introduction points."); + } else { + /* Replace encrypted with decrypted introduction points. */ + log_info(LD_REND, "Successfully decrypted introduction points."); + tor_free(intro_content); + intro_content = ipos_decrypted; + intro_size = ipos_decrypted_size; + } + } + n_intro_points = rend_parse_introduction_points(parsed, intro_content, + intro_size); + if (n_intro_points <= 0) { + log_warn(LD_REND, "Failed to parse introduction points. Either the " + "service has published a corrupt descriptor or you have " + "provided invalid authorization data."); + goto err; + } else if (n_intro_points > MAX_INTRO_POINTS) { + log_warn(LD_REND, "Found too many introduction points on a hidden " + "service descriptor for %s. This is probably a (misguided) " + "attempt to improve reliability, but it could also be an " + "attempt to do a guard enumeration attack. Rejecting.", + safe_str_client(service_id)); + + goto err; + } + } else { + log_info(LD_REND, "Descriptor does not contain any introduction points."); + parsed->intro_nodes = smartlist_new(); + } + /* We don't need the encoded/encrypted introduction points any longer. */ + tor_free(intro_content); + /* Is descriptor too old? */ + if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { + log_warn(LD_REND, "Service descriptor with service ID %s is too old.", + safe_str_client(service_id)); + goto err; + } + /* Is descriptor too far in the future? */ + if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { + log_warn(LD_REND, "Service descriptor with service ID %s is too far in " + "the future.", safe_str_client(service_id)); + goto err; + } + /* Do we have the same exact copy already in our cache? */ + tor_snprintf(key, sizeof(key), "2%s", service_id); + e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); + if (e && !strcmp(desc, e->desc)) { + log_info(LD_REND,"We already have this service descriptor %s.", + safe_str_client(service_id)); + goto okay; + } + /* Verify that we are not replacing an older descriptor. It's important to + * avoid an evil HSDir serving old descriptor. We validate if the + * timestamp is greater than and not equal because it's a rounded down + * timestamp to the hour so if the descriptor changed in the same hour, + * the rend cache failure will tells us if we have a new descriptor. */ + if (e && e->parsed->timestamp > parsed->timestamp) { + log_info(LD_REND, "We already have a new enough service descriptor for " + "service ID %s with the same desc ID and version.", + safe_str_client(service_id)); + goto okay; + } + /* Lookup our failure cache for intro point that might be unsuable. */ + validate_intro_point_failure(parsed, service_id); + /* It's now possible that our intro point list is empty, this means that + * this descriptor is useless to us because intro points have all failed + * somehow before. Discard the descriptor. */ + if (smartlist_len(parsed->intro_nodes) == 0) { + log_info(LD_REND, "Service descriptor with service ID %s, every " + "intro points are unusable. Discarding it.", + safe_str_client(service_id)); + goto err; + } + /* Now either purge the current one and replace it's content or create a + * new one and add it to the rend cache. */ + if (!e) { + e = tor_malloc_zero(sizeof(rend_cache_entry_t)); + strmap_set_lc(rend_cache, key, e); + } else { + rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); + rend_cache_failure_remove(e->parsed); + rend_service_descriptor_free(e->parsed); + tor_free(e->desc); + } + e->parsed = parsed; + e->desc = tor_malloc_zero(encoded_size + 1); + strlcpy(e->desc, desc, encoded_size + 1); + e->len = encoded_size; + rend_cache_increment_allocation(rend_cache_entry_allocation(e)); + log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", + safe_str_client(service_id), (int)encoded_size); + if (entry) { + *entry = e; + } + return 0; + + okay: + if (entry) { + *entry = e; + } + retval = 0; + + err: + rend_service_descriptor_free(parsed); + tor_free(intro_content); + return retval; +} + diff --git a/src/or/rendcache.h b/src/or/rendcache.h new file mode 100644 index 0000000000..0e8b918753 --- /dev/null +++ b/src/or/rendcache.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2015-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file rendcache.h + * \brief Header file for rendcache.c + **/ + +#ifndef TOR_RENDCACHE_H +#define TOR_RENDCACHE_H + +#include "or.h" +#include "rendcommon.h" + +/** How old do we let hidden service descriptors get before discarding + * them as too old? */ +#define REND_CACHE_MAX_AGE (2*24*60*60) +/** How wrong do we assume our clock may be when checking whether hidden + * services are too old or too new? */ +#define REND_CACHE_MAX_SKEW (24*60*60) +/** How old do we keep an intro point failure entry in the failure cache? */ +#define REND_CACHE_FAILURE_MAX_AGE (5*60) + +/* Do not allow more than this many introduction points in a hidden service + * descriptor */ +#define MAX_INTRO_POINTS 10 + +/** A cached rendezvous descriptor. */ +typedef struct rend_cache_entry_t { + size_t len; /**< Length of <b>desc</b> */ + time_t last_served; /**< When did we last write this one to somebody? + * (HSDir only) */ + char *desc; /**< Service descriptor */ + rend_service_descriptor_t *parsed; /**< Parsed value of 'desc' */ +} rend_cache_entry_t; + +/* Introduction point failure type. */ +typedef struct rend_cache_failure_intro_t { + /* When this intro point failure occured thus we allocated this object and + * cache it. */ + time_t created_ts; + rend_intro_point_failure_t failure_type; +} rend_cache_failure_intro_t; + +/** Cache failure object indexed by service ID. */ +typedef struct rend_cache_failure_t { + /* Contains rend_cache_failure_intro_t indexed by identity digest. */ + digestmap_t *intro_failures; +} rend_cache_failure_t; + +typedef enum { + REND_CACHE_TYPE_CLIENT = 1, + REND_CACHE_TYPE_SERVICE = 2, +} rend_cache_type_t; + +void rend_cache_init(void); +void rend_cache_clean(time_t now, rend_cache_type_t cache_type); +void rend_cache_failure_clean(time_t now); +void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); +void rend_cache_purge(void); +void rend_cache_free_all(void); +int rend_cache_lookup_entry(const char *query, int version, + rend_cache_entry_t **entry_out); +int rend_cache_lookup_v2_desc_as_service(const char *query, + rend_cache_entry_t **entry_out); +int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc); + +int rend_cache_store_v2_desc_as_dir(const char *desc); +int rend_cache_store_v2_desc_as_service(const char *desc); +int rend_cache_store_v2_desc_as_client(const char *desc, + const char *desc_id_base32, + const rend_data_t *rend_query, + rend_cache_entry_t **entry); +size_t rend_cache_get_total_allocation(void); + +void rend_cache_intro_failure_note(rend_intro_point_failure_t failure, + const uint8_t *identity, + const char *service_id); +void rend_cache_failure_purge(void); + +#ifdef RENDCACHE_PRIVATE + +STATIC size_t rend_cache_entry_allocation(const rend_cache_entry_t *e); +STATIC void rend_cache_entry_free(rend_cache_entry_t *e); +STATIC void rend_cache_failure_intro_entry_free(rend_cache_failure_intro_t + *entry); +STATIC void rend_cache_failure_entry_free(rend_cache_failure_t *entry); +STATIC int cache_failure_intro_lookup(const uint8_t *identity, + const char *service_id, + rend_cache_failure_intro_t + **intro_entry); +STATIC void rend_cache_decrement_allocation(size_t n); +STATIC void rend_cache_increment_allocation(size_t n); +STATIC rend_cache_failure_intro_t *rend_cache_failure_intro_entry_new( + rend_intro_point_failure_t failure); +STATIC rend_cache_failure_t *rend_cache_failure_entry_new(void); +STATIC void rend_cache_failure_remove(rend_service_descriptor_t *desc); +STATIC void cache_failure_intro_add(const uint8_t *identity, + const char *service_id, + rend_intro_point_failure_t failure); +STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc, + const char *service_id); + +STATIC void rend_cache_failure_entry_free_(void *entry); +#endif + +#endif /* TOR_RENDCACHE_H */ + diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 162e0ac53e..609c45c71d 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -38,6 +38,7 @@ void rend_client_purge_state(void) { rend_cache_purge(); + rend_cache_failure_purge(); rend_client_cancel_descriptor_fetches(); rend_client_purge_last_hid_serv_requests(); } @@ -51,7 +52,7 @@ rend_client_introcirc_has_opened(origin_circuit_t *circ) tor_assert(circ->cpath); log_info(LD_REND,"introcirc is open"); - connection_ap_attach_pending(); + connection_ap_attach_pending(1); } /** Send the establish-rendezvous cell along a rendezvous circuit. if @@ -64,11 +65,7 @@ rend_client_send_establish_rendezvous(origin_circuit_t *circ) tor_assert(circ->rend_data); log_info(LD_REND, "Sending an ESTABLISH_RENDEZVOUS cell"); - if (crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN) < 0) { - log_warn(LD_BUG, "Internal error: Couldn't produce random cookie."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - return -1; - } + crypto_rand(circ->rend_data->rend_cookie, REND_COOKIE_LEN); /* Set timestamp_dirty, because circuit_expire_building expects it, * and the rend cookie also means we've used the circ. */ @@ -141,7 +138,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, int r, v3_shift = 0; char payload[RELAY_PAYLOAD_SIZE]; char tmp[RELAY_PAYLOAD_SIZE]; - rend_cache_entry_t *entry; + rend_cache_entry_t *entry = NULL; crypt_path_t *cpath; off_t dh_offset; crypto_pk_t *intro_key = NULL; @@ -158,8 +155,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(!(rendcirc->build_state->onehop_tunnel)); #endif - if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, - &entry) < 1) { + r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1, + &entry); + /* An invalid onion address is not possible else we have a big issue. */ + tor_assert(r != -EINVAL); + if (r < 0 || !rend_client_any_intro_points_usable(entry)) { + /* If the descriptor is not found or the intro points are not usable + * anymore, trigger a fetch. */ log_info(LD_REND, "query %s didn't have valid rend desc in cache. " "Refetching descriptor.", @@ -171,6 +173,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT, introcirc->rend_data->onion_address))) { + connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); conn->state = AP_CONN_STATE_RENDDESC_WAIT; } } @@ -179,7 +182,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc, goto cleanup; } - /* first 20 bytes of payload are the hash of Bob's pk */ + /* first 20 bytes of payload are the hash of the service's pk */ intro_key = NULL; SMARTLIST_FOREACH(entry->parsed->intro_nodes, rend_intro_point_t *, intro, { @@ -469,9 +472,8 @@ rend_client_introduction_acked(origin_circuit_t *circ, /** Contains the last request times to hidden service directories for * certain queries; each key is a string consisting of the - * concatenation of a base32-encoded HS directory identity digest, a - * base32-encoded HS descriptor ID, and a hidden service address - * (without the ".onion" part); each value is a pointer to a time_t + * concatenation of a base32-encoded HS directory identity digest and + * base32-encoded HS descriptor ID; each value is a pointer to a time_t * holding the time of the last request for that descriptor ID to that * HS directory. */ static strmap_t *last_hid_serv_requests_ = NULL; @@ -487,19 +489,16 @@ get_last_hid_serv_requests(void) } #define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \ - REND_DESC_ID_V2_LEN_BASE32 + \ - REND_SERVICE_ID_LEN_BASE32) + REND_DESC_ID_V2_LEN_BASE32) /** Look up the last request time to hidden service directory <b>hs_dir</b> - * for descriptor ID <b>desc_id_base32</b> for the service specified in - * <b>rend_query</b>. If <b>set</b> is non-zero, - * assign the current time <b>now</b> and return that. Otherwise, return - * the most recent request time, or 0 if no such request has been sent - * before. */ + * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero, + * assign the current time <b>now</b> and return that. Otherwise, return the + * most recent request time, or 0 if no such request has been sent before. + */ static time_t lookup_last_hid_serv_request(routerstatus_t *hs_dir, const char *desc_id_base32, - const rend_data_t *rend_query, time_t now, int set) { char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -508,10 +507,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir, strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32), hs_dir->identity_digest, DIGEST_LEN); - tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s", + tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s", hsdir_id_base32, - desc_id_base32, - rend_query->onion_address); + desc_id_base32); /* XXX023 tor_assert(strlen(hsdir_desc_comb_id) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (set) { @@ -552,20 +550,23 @@ directory_clean_last_hid_serv_requests(time_t now) } } -/** Remove all requests related to the hidden service named - * <b>onion_address</b> from the history of times of requests to - * hidden service directories. +/** Remove all requests related to the descriptor ID <b>desc_id</b> from the + * history of times of requests to hidden service directories. + * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN. * * This is called from rend_client_note_connection_attempt_ended(), which - * must be idempotent, so any future changes to this function must leave - * it idempotent too. - */ + * must be idempotent, so any future changes to this function must leave it + * idempotent too. */ static void -purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) +purge_hid_serv_from_last_hid_serv_requests(const char *desc_id) { strmap_iter_t *iter; strmap_t *last_hid_serv_requests = get_last_hid_serv_requests(); - /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */ + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + + /* Key is stored with the base32 encoded desc_id. */ + base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id, + DIGEST_LEN); for (iter = strmap_iter_init(last_hid_serv_requests); !strmap_iter_done(iter); ) { const char *key; @@ -573,9 +574,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *onion_address) strmap_iter_get(iter, &key, &val); /* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */ if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN - - REND_SERVICE_ID_LEN_BASE32, - onion_address, - REND_SERVICE_ID_LEN_BASE32)) { + REND_DESC_ID_V2_LEN_BASE32, + desc_id_base32, + REND_DESC_ID_V2_LEN_BASE32)) { iter = strmap_iter_next_rmv(last_hid_serv_requests, iter); tor_free(val); } else { @@ -604,64 +605,53 @@ rend_client_purge_last_hid_serv_requests(void) } } -/** Determine the responsible hidden service directories for <b>desc_id</b> - * and fetch the descriptor with that ID from one of them. Only - * send a request to a hidden service directory that we have not yet tried - * during this attempt to connect to this hidden service; on success, return 1, - * in the case that no hidden service directory is left to ask for the - * descriptor, return 0, and in case of a failure -1. */ -static int -directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) +/** This returns a good valid hs dir that should be used for the given + * descriptor id. + * + * Return NULL on error else the hsdir node pointer. */ +static routerstatus_t * +pick_hsdir(const char *desc_id, const char *desc_id_base32) { smartlist_t *responsible_dirs = smartlist_new(); smartlist_t *usable_responsible_dirs = smartlist_new(); const or_options_t *options = get_options(); routerstatus_t *hs_dir; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; time_t now = time(NULL); - char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; -#ifdef ENABLE_TOR2WEB_MODE - const int tor2web_mode = options->Tor2webMode; - const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; -#else - const int how_to_fetch = DIRIND_ANONYMOUS; -#endif int excluded_some; - tor_assert(desc_id); - tor_assert(rend_query); - /* Determine responsible dirs. Even if we can't get all we want, - * work with the ones we have. If it's empty, we'll notice below. */ - hid_serv_get_responsible_directories(responsible_dirs, desc_id); - base32_encode(desc_id_base32, sizeof(desc_id_base32), - desc_id, DIGEST_LEN); + tor_assert(desc_id); + tor_assert(desc_id_base32); - /* Only select those hidden service directories to which we did not send - * a request recently and for which we have a router descriptor here. */ + /* Determine responsible dirs. Even if we can't get all we want, work with + * the ones we have. If it's empty, we'll notice below. */ + hid_serv_get_responsible_directories(responsible_dirs, desc_id); /* Clean request history first. */ directory_clean_last_hid_serv_requests(now); - SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, { - time_t last = lookup_last_hid_serv_request( - dir, desc_id_base32, rend_query, 0, 0); - const node_t *node = node_get_by_id(dir->identity_digest); - if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || - !node || !node_has_descriptor(node)) { - SMARTLIST_DEL_CURRENT(responsible_dirs, dir); - continue; - } - if (! routerset_contains_node(options->ExcludeNodes, node)) { - smartlist_add(usable_responsible_dirs, dir); - } - }); + /* Only select those hidden service directories to which we did not send a + * request recently and for which we have a router descriptor here. */ + SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) { + time_t last = lookup_last_hid_serv_request(dir, desc_id_base32, + 0, 0); + const node_t *node = node_get_by_id(dir->identity_digest); + if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now || + !node || !node_has_descriptor(node)) { + SMARTLIST_DEL_CURRENT(responsible_dirs, dir); + continue; + } + if (!routerset_contains_node(options->ExcludeNodes, node)) { + smartlist_add(usable_responsible_dirs, dir); + } + } SMARTLIST_FOREACH_END(dir); excluded_some = smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs); hs_dir = smartlist_choose(usable_responsible_dirs); - if (! hs_dir && ! options->StrictNodes) + if (!hs_dir && !options->StrictNodes) { hs_dir = smartlist_choose(responsible_dirs); + } smartlist_free(responsible_dirs); smartlist_free(usable_responsible_dirs); @@ -674,23 +664,69 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) "requested hidden service: they are all either down or " "excluded, and StrictNodes is set."); } - return 0; + } else { + /* Remember that we are requesting a descriptor from this hidden service + * directory now. */ + lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1); } - /* Remember that we are requesting a descriptor from this hidden service - * directory now. */ - lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1); + return hs_dir; +} - /* Encode descriptor cookie for logging purposes. */ +/** Determine the responsible hidden service directories for <b>desc_id</b> + * and fetch the descriptor with that ID from one of them. Only + * send a request to a hidden service directory that we have not yet tried + * during this attempt to connect to this hidden service; on success, return 1, + * in the case that no hidden service directory is left to ask for the + * descriptor, return 0, and in case of a failure -1. */ +static int +directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query, + routerstatus_t *rs_hsdir) +{ + routerstatus_t *hs_dir = rs_hsdir; + char *hsdir_fp; + char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; + char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; +#ifdef ENABLE_TOR2WEB_MODE + const int tor2web_mode = get_options()->Tor2webMode; + const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS; +#else + const int how_to_fetch = DIRIND_ANONYMOUS; +#endif + + tor_assert(desc_id); + + base32_encode(desc_id_base32, sizeof(desc_id_base32), + desc_id, DIGEST_LEN); + + /* Automatically pick an hs dir if none given. */ + if (!rs_hsdir) { + hs_dir = pick_hsdir(desc_id, desc_id_base32); + if (!hs_dir) { + /* No suitable hs dir can be found, stop right now. */ + return 0; + } + } + + /* Add a copy of the HSDir identity digest to the query so we can track it + * on the control port. */ + hsdir_fp = tor_memdup(hs_dir->identity_digest, + sizeof(hs_dir->identity_digest)); + smartlist_add(rend_query->hsdirs_fp, hsdir_fp); + + /* Encode descriptor cookie for logging purposes. Also, if the cookie is + * malformed, no fetch is triggered thus this needs to be done before the + * fetch request. */ if (rend_query->auth_type != REND_NO_AUTH) { if (base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), - rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) { + rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN, + 0)<0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); return 0; } - /* Remove == signs and newline. */ - descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0'; + /* Remove == signs. */ + descriptor_cookie_base64[strlen(descriptor_cookie_base64)-2] = '\0'; } else { strlcpy(descriptor_cookie_base64, "(none)", sizeof(descriptor_cookie_base64)); @@ -721,16 +757,143 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query) return 1; } +/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are + * given, they will be used instead. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query, + smartlist_t *hsdirs) +{ + int ret; + + tor_assert(rend_query); + + if (!hsdirs) { + ret = directory_get_from_hs_dir(desc_id, rend_query, NULL); + goto end; /* either success or failure, but we're done */ + } + + /* Using the given hsdir list, trigger a fetch on each of them. */ + SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) { + /* This should always be a success. */ + ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir); + tor_assert(ret); + } SMARTLIST_FOREACH_END(hs_dir); + + /* Everything went well. */ + ret = 0; + + end: + return ret; +} + +/** Fetch a v2 descriptor using the onion address in the given query object. + * This will compute the descriptor id for each replicas and fetch it on the + * given hsdir(s) if any or the responsible ones that are choosen + * automatically. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +static int +fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs) +{ + char descriptor_id[DIGEST_LEN]; + int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; + int i, tries_left, ret; + + tor_assert(query); + + /* Randomly iterate over the replicas until a descriptor can be fetched + * from one of the consecutive nodes, or no options are left. */ + for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) { + replicas_left_to_try[i] = i; + } + + tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; + while (tries_left > 0) { + int rand = crypto_rand_int(tries_left); + int chosen_replica = replicas_left_to_try[rand]; + replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; + + ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address, + query->auth_type == REND_STEALTH_AUTH ? + query->descriptor_cookie : NULL, + time(NULL), chosen_replica); + if (ret < 0) { + /* Normally, on failure the descriptor_id is untouched but let's be + * safe in general in case the function changes at some point. */ + goto end; + } + + if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica], + sizeof(descriptor_id)) != 0) { + /* Not equal from what we currently have so purge the last hid serv + * request cache and update the descriptor ID with the new value. */ + purge_hid_serv_from_last_hid_serv_requests( + query->descriptor_id[chosen_replica]); + memcpy(query->descriptor_id[chosen_replica], descriptor_id, + sizeof(query->descriptor_id[chosen_replica])); + } + + /* Trigger the fetch with the computed descriptor ID. */ + ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs); + if (ret != 0) { + /* Either on success or failure, as long as we tried a fetch we are + * done here. */ + goto end; + } + } + + /* If we come here, there are no hidden service directories left. */ + log_info(LD_REND, "Could not pick one of the responsible hidden " + "service directories to fetch descriptors, because " + "we already tried them all unsuccessfully."); + ret = 0; + + end: + memwipe(descriptor_id, 0, sizeof(descriptor_id)); + return ret; +} + +/** Fetch a v2 descriptor using the given query. If any hsdir are specified, + * use them for the fetch. + * + * On success, 1 is returned. If no hidden service is left to ask, return 0. + * On error, -1 is returned. */ +int +rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs) +{ + int ret; + + tor_assert(query); + + /* Depending on what's available in the rend data query object, we will + * trigger a fetch by HS address or using a descriptor ID. */ + + if (query->onion_address[0] != '\0') { + ret = fetch_v2_desc_by_addr(query, hsdirs); + } else if (!tor_digest_is_zero(query->desc_id_fetch)) { + ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs); + } else { + /* Query data is invalid. */ + ret = -1; + goto error; + } + + error: + return ret; +} + /** Unless we already have a descriptor for <b>rend_query</b> with at least * one (possibly) working introduction point in it, start a connection to a * hidden service directory to fetch a v2 rendezvous service descriptor. */ void -rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) +rend_client_refetch_v2_renddesc(rend_data_t *rend_query) { - char descriptor_id[DIGEST_LEN]; - int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS]; - int i, tries_left; rend_cache_entry_t *e = NULL; + tor_assert(rend_query); /* Are we configured to fetch descriptors? */ if (!get_options()->FetchHidServDescriptors) { @@ -739,7 +902,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) return; } /* Before fetching, check if we already have a usable descriptor here. */ - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 && + if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 && rend_client_any_intro_points_usable(e)) { log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we " "already have a usable descriptor here. Not fetching."); @@ -747,44 +910,11 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query) } log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s", safe_str_client(rend_query->onion_address)); - /* Randomly iterate over the replicas until a descriptor can be fetched - * from one of the consecutive nodes, or no options are left. */ - tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; - for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) - replicas_left_to_try[i] = i; - while (tries_left > 0) { - int rand = crypto_rand_int(tries_left); - int chosen_replica = replicas_left_to_try[rand]; - replicas_left_to_try[rand] = replicas_left_to_try[--tries_left]; - - if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address, - rend_query->auth_type == REND_STEALTH_AUTH ? - rend_query->descriptor_cookie : NULL, - time(NULL), chosen_replica) < 0) { - log_warn(LD_REND, "Internal error: Computing v2 rendezvous " - "descriptor ID did not succeed."); - /* - * Hmm, can this write anything to descriptor_id and still fail? - * Let's clear it just to be safe. - * - * From here on, any returns should goto done which clears - * descriptor_id so we don't leave key-derived material on the stack. - */ - goto done; - } - if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0) - goto done; /* either success or failure, but we're done */ - } - /* If we come here, there are no hidden service directories left. */ - log_info(LD_REND, "Could not pick one of the responsible hidden " - "service directories to fetch descriptors, because " - "we already tried them all unsuccessfully."); - /* Close pending connections. */ - rend_client_desc_trynow(rend_query->onion_address); - - done: - memwipe(descriptor_id, 0, sizeof(descriptor_id)); + rend_client_fetch_v2_desc(rend_query, NULL); + /* We don't need to look the error code because either on failure or + * success, the necessary steps to continue the HS connection will be + * triggered once the descriptor arrives or if all fetch failed. */ return; } @@ -845,7 +975,7 @@ rend_client_cancel_descriptor_fetches(void) */ int rend_client_report_intro_point_failure(extend_info_t *failed_intro, - const rend_data_t *rend_query, + rend_data_t *rend_query, unsigned int failure_type) { int i, r; @@ -853,17 +983,26 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, connection_t *conn; r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent); - if (r<0) { - log_warn(LD_BUG, "Malformed service ID %s.", - escaped_safe_str_client(rend_query->onion_address)); - return -1; - } - if (r==0) { - log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", - escaped_safe_str_client(rend_query->onion_address)); - rend_client_refetch_v2_renddesc(rend_query); - return 0; + if (r < 0) { + /* Either invalid onion address or cache entry not found. */ + switch (-r) { + case EINVAL: + log_warn(LD_BUG, "Malformed service ID %s.", + escaped_safe_str_client(rend_query->onion_address)); + return -1; + case ENOENT: + log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.", + escaped_safe_str_client(rend_query->onion_address)); + rend_client_refetch_v2_renddesc(rend_query); + return 0; + default: + log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r); + return -1; + } } + /* The intro points are not checked here if they are usable or not because + * this is called when an intro point circuit is closed thus there must be + * at least one intro point that is usable and is about to be flagged. */ for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) { rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i); @@ -876,6 +1015,9 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, tor_fragile_assert(); /* fall through */ case INTRO_POINT_FAILURE_GENERIC: + rend_cache_intro_failure_note(failure_type, + (uint8_t *)failed_intro->identity_digest, + rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); break; @@ -891,6 +1033,10 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, intro->unreachable_count, zap_intro_point ? " Removing from descriptor.": ""); if (zap_intro_point) { + rend_cache_intro_failure_note( + failure_type, + (uint8_t *) failed_intro->identity_digest, + rend_query->onion_address); rend_intro_point_free(intro); smartlist_del(ent->parsed->intro_nodes, i); } @@ -908,9 +1054,11 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro, rend_client_refetch_v2_renddesc(rend_query); /* move all pending streams back to renddesc_wait */ + /* NOTE: We can now do this faster, if we use pending_entry_connections */ while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT, rend_query->onion_address))) { + connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn)); conn->state = AP_CONN_STATE_RENDDESC_WAIT; } @@ -946,9 +1094,9 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request, circ->base_.timestamp_dirty = time(NULL); /* From a path bias point of view, this circuit is now successfully used. - * Waiting any longer opens us up to attacks from Bob. He could induce - * Alice to attempt to connect to his hidden service and never reply - * to her rend requests */ + * Waiting any longer opens us up to attacks from malicious hidden services. + * They could induce the client to attempt to connect to their hidden + * service and never reply to the client's rend requests */ pathbias_mark_use_success(circ); /* XXXX This is a pretty brute-force approach. It'd be better to @@ -956,11 +1104,11 @@ rend_client_rendezvous_acked(origin_circuit_t *circ, const uint8_t *request, * than trying to attach them all. See comments bug 743. */ /* If we already have the introduction circuit built, make sure we send * the INTRODUCE cell _now_ */ - connection_ap_attach_pending(); + connection_ap_attach_pending(1); return 0; } -/** Bob sent us a rendezvous cell; join the circuits. */ +/** The service sent us a rendezvous cell; join the circuits. */ int rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, size_t request_len) @@ -985,7 +1133,8 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, log_info(LD_REND,"Got RENDEZVOUS2 cell from hidden service."); - /* first DH_KEY_LEN bytes are g^y from bob. Finish the dh handshake...*/ + /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh + * handshake...*/ tor_assert(circ->build_state); tor_assert(circ->build_state->pending_final_cpath); hop = circ->build_state->pending_final_cpath; @@ -1014,7 +1163,7 @@ rend_client_receive_rendezvous(origin_circuit_t *circ, const uint8_t *request, circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_C_REND_JOINED); hop->state = CPATH_STATE_OPEN; /* set the windows to default. these are the windows - * that alice thinks bob has. + * that the client thinks the service has. */ hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; @@ -1062,7 +1211,7 @@ rend_client_desc_trynow(const char *query) continue; assert_connection_ok(base_conn, now); if (rend_cache_lookup_entry(rend_data->onion_address, -1, - &entry) == 1 && + &entry) == 0 && rend_client_any_intro_points_usable(entry)) { /* either this fetch worked, or it failed but there was a * valid entry from before which we should reuse */ @@ -1075,38 +1224,34 @@ rend_client_desc_trynow(const char *query) base_conn->timestamp_lastread = now; base_conn->timestamp_lastwritten = now; - if (connection_ap_handshake_attach_circuit(conn) < 0) { - /* it will never work */ - log_warn(LD_REND,"Rendezvous attempt failed. Closing."); - if (!base_conn->marked_for_close) - connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH); - } + connection_ap_mark_as_pending_circuit(conn); } else { /* 404, or fetch didn't get that far */ log_notice(LD_REND,"Closing stream for '%s.onion': hidden service is " "unavailable (try again later).", safe_str_client(query)); connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED); - rend_client_note_connection_attempt_ended(query); + rend_client_note_connection_attempt_ended(rend_data); } } SMARTLIST_FOREACH_END(base_conn); } -/** Clear temporary state used only during an attempt to connect to - * the hidden service named <b>onion_address</b>. Called when a - * connection attempt has ended; it is possible for this to be called - * multiple times while handling an ended connection attempt, and - * any future changes to this function must ensure it remains - * idempotent. - */ +/** Clear temporary state used only during an attempt to connect to the + * hidden service with <b>rend_data</b>. Called when a connection attempt + * has ended; it is possible for this to be called multiple times while + * handling an ended connection attempt, and any future changes to this + * function must ensure it remains idempotent. */ void -rend_client_note_connection_attempt_ended(const char *onion_address) +rend_client_note_connection_attempt_ended(const rend_data_t *rend_data) { + unsigned int have_onion = 0; rend_cache_entry_t *cache_entry = NULL; - rend_cache_lookup_entry(onion_address, -1, &cache_entry); - log_info(LD_REND, "Connection attempt for %s has ended; " - "cleaning up temporary state.", - safe_str_client(onion_address)); + if (*rend_data->onion_address != '\0') { + /* Ignore return value; we find an entry, or we don't. */ + (void) rend_cache_lookup_entry(rend_data->onion_address, -1, + &cache_entry); + have_onion = 1; + } /* Clear the timed_out flag on all remaining intro points for this HS. */ if (cache_entry != NULL) { @@ -1116,7 +1261,20 @@ rend_client_note_connection_attempt_ended(const char *onion_address) } /* Remove the HS's entries in last_hid_serv_requests. */ - purge_hid_serv_from_last_hid_serv_requests(onion_address); + if (have_onion) { + unsigned int replica; + for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); + replica++) { + const char *desc_id = rend_data->descriptor_id[replica]; + purge_hid_serv_from_last_hid_serv_requests(desc_id); + } + log_info(LD_REND, "Connection attempt for %s has ended; " + "cleaning up temporary state.", + safe_str_client(rend_data->onion_address)); + } else { + /* We only have an ID for a fetch. Probably used by HSFETCH. */ + purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch); + } } /** Return a newly allocated extend_info_t* for a randomly chosen introduction @@ -1126,13 +1284,17 @@ rend_client_note_connection_attempt_ended(const char *onion_address) extend_info_t * rend_client_get_random_intro(const rend_data_t *rend_query) { + int ret; extend_info_t *result; rend_cache_entry_t *entry; - if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) { - log_warn(LD_REND, - "Query '%s' didn't have valid rend desc in cache. Failing.", - safe_str_client(rend_query->onion_address)); + ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry); + if (ret < 0 || !rend_client_any_intro_points_usable(entry)) { + log_warn(LD_REND, + "Query '%s' didn't have valid rend desc in cache. Failing.", + safe_str_client(rend_query->onion_address)); + /* XXX: Should we refetch the descriptor here if the IPs are not usable + * anymore ?. */ return NULL; } @@ -1203,11 +1365,19 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, smartlist_del(usable_nodes, i); goto again; } +#ifdef ENABLE_TOR2WEB_MODE + new_extend_info = extend_info_from_node(node, options->Tor2webMode); +#else new_extend_info = extend_info_from_node(node, 0); +#endif if (!new_extend_info) { + const char *alternate_reason = ""; +#ifdef ENABLE_TOR2WEB_MODE + alternate_reason = ", or we cannot connect directly to it"; +#endif log_info(LD_REND, "We don't have a descriptor for the intro-point relay " - "'%s'; trying another.", - extend_info_describe(intro->extend_info)); + "'%s'%s; trying another.", + extend_info_describe(intro->extend_info), alternate_reason); smartlist_del(usable_nodes, i); goto again; } else { diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 098c61d0a1..e90dac07ab 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,6 +12,8 @@ #ifndef TOR_RENDCLIENT_H #define TOR_RENDCLIENT_H +#include "rendcache.h" + void rend_client_purge_state(void); void rend_client_introcirc_has_opened(origin_circuit_t *circ); @@ -19,16 +21,13 @@ void rend_client_rendcirc_has_opened(origin_circuit_t *circ); int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len); -void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query); +void rend_client_refetch_v2_renddesc(rend_data_t *rend_query); +int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs); void rend_client_cancel_descriptor_fetches(void); void rend_client_purge_last_hid_serv_requests(void); -#define INTRO_POINT_FAILURE_GENERIC 0 -#define INTRO_POINT_FAILURE_TIMEOUT 1 -#define INTRO_POINT_FAILURE_UNREACHABLE 2 - int rend_client_report_intro_point_failure(extend_info_t *failed_intro, - const rend_data_t *rend_query, + rend_data_t *rend_query, unsigned int failure_type); int rend_client_rendezvous_acked(origin_circuit_t *circ, @@ -39,7 +38,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ, size_t request_len); void rend_client_desc_trynow(const char *query); -void rend_client_note_connection_attempt_ended(const char *onion_address); +void rend_client_note_connection_attempt_ended(const rend_data_t *rend_data); extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query); int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry); @@ -51,7 +50,6 @@ int rend_parse_service_authorization(const or_options_t *options, rend_service_authorization_t *rend_client_lookup_service_authorization( const char *onion_address); void rend_service_authorization_free_all(void); -rend_data_t *rend_data_dup(const rend_data_t *request); #endif diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 6698f2feaf..438fbc4d9a 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -11,13 +11,16 @@ #include "or.h" #include "circuitbuild.h" #include "config.h" +#include "control.h" #include "rendclient.h" #include "rendcommon.h" #include "rendmid.h" #include "rendservice.h" #include "rephist.h" +#include "router.h" #include "routerlist.h" #include "routerparse.h" +#include "networkstatus.h" /** Return 0 if one and two are the same service ids, else -1 or 1 */ int @@ -155,10 +158,10 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, } /* Calculate current time-period. */ time_period = get_time_period(now, 0, service_id_binary); - /* Calculate secret-id-part = h(time-period | replica). */ + /* Calculate secret-id-part = h(time-period | desc-cookie | replica). */ get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie, replica); - /* Calculate descriptor ID. */ + /* Calculate descriptor ID: H(permanent-id | secret-id-part) */ rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part); return 0; } @@ -268,11 +271,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out, tor_assert(client_cookies && smartlist_len(client_cookies) > 0); /* Generate session key. */ - if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) { - log_warn(LD_REND, "Unable to generate random session key to encrypt " - "introduction point string."); - goto done; - } + crypto_rand(session_key, CIPHER_KEY_LEN); /* Determine length of encrypted introduction points including session * keys. */ @@ -334,11 +333,7 @@ rend_encrypt_v2_intro_points_basic(char **encrypted_out, REND_BASIC_AUTH_CLIENT_MULTIPLE; i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) { client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN); - if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) { - log_warn(LD_REND, "Unable to generate fake client entry."); - tor_free(client_part); - goto done; - } + crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN); smartlist_add(encrypted_session_keys, client_part); } /* Sort smartlist and put elements in result in order. */ @@ -461,6 +456,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, smartlist_t *client_cookies) { char service_id[DIGEST_LEN]; + char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1]; uint32_t time_period; char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL, *descriptor_cookie = NULL; @@ -529,7 +525,8 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, } /* Base64-encode introduction points. */ ipos_base64 = tor_calloc(ipos_len, 2); - if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) { + if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len, + BASE64_ENCODE_MULTILINE)<0) { log_warn(LD_REND, "Could not encode introduction point string to " "base64. length=%d", (int)ipos_len); tor_free(ipos_base64); @@ -646,7 +643,6 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, rend_encoded_v2_service_descriptor_free(enc); goto err; } - desc_str[written++] = '\n'; desc_str[written++] = 0; /* Check if we can parse our own descriptor. */ if (!rend_desc_v2_is_parsable(enc)) { @@ -655,6 +651,11 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, goto err; } smartlist_add(descs_out, enc); + /* Add the uploaded descriptor to the local service's descriptor cache */ + rend_cache_store_v2_desc_as_service(enc->desc_str); + base32_encode(service_id_base32, sizeof(service_id_base32), + service_id, REND_SERVICE_ID_LEN); + control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k); } log_info(LD_REND, "Successfully encoded a v2 descriptor and " @@ -687,225 +688,6 @@ rend_get_service_id(crypto_pk_t *pk, char *out) return 0; } -/* ==== Rendezvous service descriptor cache. */ - -/** How old do we let hidden service descriptors get before discarding - * them as too old? */ -#define REND_CACHE_MAX_AGE (2*24*60*60) -/** How wrong do we assume our clock may be when checking whether hidden - * services are too old or too new? */ -#define REND_CACHE_MAX_SKEW (24*60*60) - -/** Map from service id (as generated by rend_get_service_id) to - * rend_cache_entry_t. */ -static strmap_t *rend_cache = NULL; - -/** Map from descriptor id to rend_cache_entry_t; only for hidden service - * directories. */ -static digestmap_t *rend_cache_v2_dir = NULL; - -/** DOCDOC */ -static size_t rend_cache_total_allocation = 0; - -/** Initializes the service descriptor cache. - */ -void -rend_cache_init(void) -{ - rend_cache = strmap_new(); - rend_cache_v2_dir = digestmap_new(); -} - -/** Return the approximate number of bytes needed to hold <b>e</b>. */ -static size_t -rend_cache_entry_allocation(const rend_cache_entry_t *e) -{ - if (!e) - return 0; - - /* This doesn't count intro_nodes or key size */ - return sizeof(*e) + e->len + sizeof(*e->parsed); -} - -/** DOCDOC */ -size_t -rend_cache_get_total_allocation(void) -{ - return rend_cache_total_allocation; -} - -/** Decrement the total bytes attributed to the rendezvous cache by n. */ -static void -rend_cache_decrement_allocation(size_t n) -{ - static int have_underflowed = 0; - - if (rend_cache_total_allocation >= n) { - rend_cache_total_allocation -= n; - } else { - rend_cache_total_allocation = 0; - if (! have_underflowed) { - have_underflowed = 1; - log_warn(LD_BUG, "Underflow in rend_cache_decrement_allocation"); - } - } -} - -/** Increase the total bytes attributed to the rendezvous cache by n. */ -static void -rend_cache_increment_allocation(size_t n) -{ - static int have_overflowed = 0; - if (rend_cache_total_allocation <= SIZE_MAX - n) { - rend_cache_total_allocation += n; - } else { - rend_cache_total_allocation = SIZE_MAX; - if (! have_overflowed) { - have_overflowed = 1; - log_warn(LD_BUG, "Overflow in rend_cache_increment_allocation"); - } - } -} - -/** Helper: free storage held by a single service descriptor cache entry. */ -static void -rend_cache_entry_free(rend_cache_entry_t *e) -{ - if (!e) - return; - rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); - rend_service_descriptor_free(e->parsed); - tor_free(e->desc); - tor_free(e); -} - -/** Helper: deallocate a rend_cache_entry_t. (Used with strmap_free(), which - * requires a function pointer whose argument is void*). */ -static void -rend_cache_entry_free_(void *p) -{ - rend_cache_entry_free(p); -} - -/** Free all storage held by the service descriptor cache. */ -void -rend_cache_free_all(void) -{ - strmap_free(rend_cache, rend_cache_entry_free_); - digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_); - rend_cache = NULL; - rend_cache_v2_dir = NULL; - rend_cache_total_allocation = 0; -} - -/** Removes all old entries from the service descriptor cache. - */ -void -rend_cache_clean(time_t now) -{ - strmap_iter_t *iter; - const char *key; - void *val; - rend_cache_entry_t *ent; - time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; - for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) { - strmap_iter_get(iter, &key, &val); - ent = (rend_cache_entry_t*)val; - if (ent->parsed->timestamp < cutoff) { - iter = strmap_iter_next_rmv(rend_cache, iter); - rend_cache_entry_free(ent); - } else { - iter = strmap_iter_next(rend_cache, iter); - } - } -} - -/** Remove ALL entries from the rendezvous service descriptor cache. - */ -void -rend_cache_purge(void) -{ - if (rend_cache) { - log_info(LD_REND, "Purging HS descriptor cache"); - strmap_free(rend_cache, rend_cache_entry_free_); - } - rend_cache = strmap_new(); -} - -/** Remove all old v2 descriptors and those for which this hidden service - * directory is not responsible for any more. - * - * If at all possible, remove at least <b>force_remove</b> bytes of data. - */ -void -rend_cache_clean_v2_descs_as_dir(time_t now, size_t force_remove) -{ - digestmap_iter_t *iter; - time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW; - const int LAST_SERVED_CUTOFF_STEP = 1800; - time_t last_served_cutoff = cutoff; - size_t bytes_removed = 0; - do { - for (iter = digestmap_iter_init(rend_cache_v2_dir); - !digestmap_iter_done(iter); ) { - const char *key; - void *val; - rend_cache_entry_t *ent; - digestmap_iter_get(iter, &key, &val); - ent = val; - if (ent->parsed->timestamp < cutoff || - ent->last_served < last_served_cutoff || - !hid_serv_responsible_for_desc_id(key)) { - char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN); - log_info(LD_REND, "Removing descriptor with ID '%s' from cache", - safe_str_client(key_base32)); - bytes_removed += rend_cache_entry_allocation(ent); - iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter); - rend_cache_entry_free(ent); - } else { - iter = digestmap_iter_next(rend_cache_v2_dir, iter); - } - } - - /* In case we didn't remove enough bytes, advance the cutoff a little. */ - last_served_cutoff += LAST_SERVED_CUTOFF_STEP; - if (last_served_cutoff > now) - break; - } while (bytes_removed < force_remove); -} - -/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and - * <b>c</b> (included) in a circular digest ring; returns 1 if this is the - * case, and 0 otherwise. - */ -int -rend_id_is_in_interval(const char *a, const char *b, const char *c) -{ - int a_b, b_c, c_a; - tor_assert(a); - tor_assert(b); - tor_assert(c); - - /* There are five cases in which a is outside the interval ]b,c]: */ - a_b = tor_memcmp(a,b,DIGEST_LEN); - if (a_b == 0) - return 0; /* 1. a == b (b is excluded) */ - b_c = tor_memcmp(b,c,DIGEST_LEN); - if (b_c == 0) - return 0; /* 2. b == c (interval is empty) */ - else if (a_b <= 0 && b_c < 0) - return 0; /* 3. a b c */ - c_a = tor_memcmp(c,a,DIGEST_LEN); - if (c_a < 0 && a_b <= 0) - return 0; /* 4. c a b */ - else if (b_c < 0 && c_a < 0) - return 0; /* 5. b c a */ - - /* In the other cases (a c b; b a c; c b a), a is inside the interval. */ - return 1; -} - /** Return true iff <b>query</b> is a syntactically valid service ID (as * generated by rend_get_service_id). */ int @@ -920,373 +702,24 @@ rend_valid_service_id(const char *query) return 1; } -/** If we have a cached rend_cache_entry_t for the service ID <b>query</b> - * with <b>version</b>, set *<b>e</b> to that entry and return 1. - * Else return 0. If <b>version</b> is nonnegative, only return an entry - * in that descriptor format version. Otherwise (if <b>version</b> is - * negative), return the most recent format we have. - */ +/** Return true iff <b>query</b> is a syntactically valid descriptor ID. + * (as generated by rend_get_descriptor_id_bytes). */ int -rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e) +rend_valid_descriptor_id(const char *query) { - char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */ - tor_assert(rend_cache); - if (!rend_valid_service_id(query)) - return -1; - *e = NULL; - if (version != 0) { - tor_snprintf(key, sizeof(key), "2%s", query); - *e = strmap_get_lc(rend_cache, key); + if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) { + goto invalid; } - if (!*e && version != 2) { - tor_snprintf(key, sizeof(key), "0%s", query); - *e = strmap_get_lc(rend_cache, key); + if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) { + goto invalid; } - if (!*e) - return 0; - tor_assert((*e)->parsed && (*e)->parsed->intro_nodes); - /* XXX023 hack for now, to return "not found" if there are no intro - * points remaining. See bug 997. */ - if (! rend_client_any_intro_points_usable(*e)) - return 0; + return 1; -} -/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and - * copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on - * well-formed-but-not-found, and -1 on failure. - */ -int -rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc) -{ - rend_cache_entry_t *e; - char desc_id_digest[DIGEST_LEN]; - tor_assert(rend_cache_v2_dir); - if (base32_decode(desc_id_digest, DIGEST_LEN, - desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) { - log_fn(LOG_PROTOCOL_WARN, LD_REND, - "Rejecting v2 rendezvous descriptor request -- descriptor ID " - "contains illegal characters: %s", - safe_str(desc_id)); - return -1; - } - /* Lookup descriptor and return. */ - e = digestmap_get(rend_cache_v2_dir, desc_id_digest); - if (e) { - *desc = e->desc; - e->last_served = approx_time(); - return 1; - } + invalid: return 0; } -/* Do not allow more than this many introduction points in a hidden service - * descriptor */ -#define MAX_INTRO_POINTS 10 - -/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the - * local rend cache. Don't attempt to decrypt the included list of introduction - * points (as we don't have a descriptor cookie for it). - * - * If we have a newer descriptor with the same ID, ignore this one. - * If we have an older descriptor with the same ID, replace it. - * - * Return an appropriate rend_cache_store_status_t. - */ -rend_cache_store_status_t -rend_cache_store_v2_desc_as_dir(const char *desc) -{ - const or_options_t *options = get_options(); - rend_service_descriptor_t *parsed; - char desc_id[DIGEST_LEN]; - char *intro_content; - size_t intro_size; - size_t encoded_size; - char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; - int number_parsed = 0, number_stored = 0; - const char *current_desc = desc; - const char *next_desc; - rend_cache_entry_t *e; - time_t now = time(NULL); - tor_assert(rend_cache_v2_dir); - tor_assert(desc); - if (!hid_serv_acting_as_directory()) { - /* Cannot store descs, because we are (currently) not acting as - * hidden service directory. */ - log_info(LD_REND, "Cannot store descs: Not acting as hs dir"); - return RCS_NOTDIR; - } - while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, - &intro_size, &encoded_size, - &next_desc, current_desc, 1) >= 0) { - number_parsed++; - /* We don't care about the introduction points. */ - tor_free(intro_content); - /* For pretty log statements. */ - base32_encode(desc_id_base32, sizeof(desc_id_base32), - desc_id, DIGEST_LEN); - /* Is desc ID in the range that we are (directly or indirectly) responsible - * for? */ - if (!hid_serv_responsible_for_desc_id(desc_id)) { - log_info(LD_REND, "Service descriptor with desc ID %s is not in " - "interval that we are responsible for.", - safe_str_client(desc_id_base32)); - goto skip; - } - /* Is descriptor too old? */ - if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { - log_info(LD_REND, "Service descriptor with desc ID %s is too old.", - safe_str(desc_id_base32)); - goto skip; - } - /* Is descriptor too far in the future? */ - if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { - log_info(LD_REND, "Service descriptor with desc ID %s is too far in the " - "future.", - safe_str(desc_id_base32)); - goto skip; - } - /* Do we already have a newer descriptor? */ - e = digestmap_get(rend_cache_v2_dir, desc_id); - if (e && e->parsed->timestamp > parsed->timestamp) { - log_info(LD_REND, "We already have a newer service descriptor with the " - "same desc ID %s and version.", - safe_str(desc_id_base32)); - goto skip; - } - /* Do we already have this descriptor? */ - if (e && !strcmp(desc, e->desc)) { - log_info(LD_REND, "We already have this service descriptor with desc " - "ID %s.", safe_str(desc_id_base32)); - goto skip; - } - /* Store received descriptor. */ - if (!e) { - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - digestmap_set(rend_cache_v2_dir, desc_id, e); - /* Treat something just uploaded as having been served a little - * while ago, so that flooding with new descriptors doesn't help - * too much. - */ - e->last_served = approx_time() - 3600; - } else { - rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); - rend_service_descriptor_free(e->parsed); - tor_free(e->desc); - } - e->parsed = parsed; - e->desc = tor_strndup(current_desc, encoded_size); - e->len = encoded_size; - rend_cache_increment_allocation(rend_cache_entry_allocation(e)); - log_info(LD_REND, "Successfully stored service descriptor with desc ID " - "'%s' and len %d.", - safe_str(desc_id_base32), (int)encoded_size); - - /* Statistics: Note down this potentially new HS. */ - if (options->HiddenServiceStatistics) { - rep_hist_stored_maybe_new_hs(e->parsed->pk); - } - - number_stored++; - goto advance; - skip: - rend_service_descriptor_free(parsed); - advance: - /* advance to next descriptor, if available. */ - current_desc = next_desc; - /* check if there is a next descriptor. */ - if (!current_desc || - strcmpstart(current_desc, "rendezvous-service-descriptor ")) - break; - } - if (!number_parsed) { - log_info(LD_REND, "Could not parse any descriptor."); - return RCS_BADDESC; - } - log_info(LD_REND, "Parsed %d and added %d descriptor%s.", - number_parsed, number_stored, number_stored != 1 ? "s" : ""); - return RCS_OKAY; -} - -/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list - * of introduction points with <b>descriptor_cookie</b> (which may also be - * <b>NULL</b> if decryption is not necessary), and store the descriptor to - * the local cache under its version and service id. - * - * If we have a newer v2 descriptor with the same ID, ignore this one. - * If we have an older descriptor with the same ID, replace it. - * If the descriptor's service ID does not match - * <b>rend_query</b>-\>onion_address, reject it. - * - * If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>, - * reject it. - * - * Return an appropriate rend_cache_store_status_t. - */ -rend_cache_store_status_t -rend_cache_store_v2_desc_as_client(const char *desc, - const char *desc_id_base32, - const rend_data_t *rend_query) -{ - /*XXXX this seems to have a bit of duplicate code with - * rend_cache_store_v2_desc_as_dir(). Fix that. */ - /* Though having similar elements, both functions were separated on - * purpose: - * - dirs don't care about encoded/encrypted introduction points, clients - * do. - * - dirs store descriptors in a separate cache by descriptor ID, whereas - * clients store them by service ID; both caches are different data - * structures and have different access methods. - * - dirs store a descriptor only if they are responsible for its ID, - * clients do so in every way (because they have requested it before). - * - dirs can process multiple concatenated descriptors which is required - * for replication, whereas clients only accept a single descriptor. - * Thus, combining both methods would result in a lot of if statements - * which probably would not improve, but worsen code readability. -KL */ - rend_service_descriptor_t *parsed = NULL; - char desc_id[DIGEST_LEN]; - char *intro_content = NULL; - size_t intro_size; - size_t encoded_size; - const char *next_desc; - time_t now = time(NULL); - char key[REND_SERVICE_ID_LEN_BASE32+2]; - char service_id[REND_SERVICE_ID_LEN_BASE32+1]; - char want_desc_id[DIGEST_LEN]; - rend_cache_entry_t *e; - rend_cache_store_status_t retval = RCS_BADDESC; - tor_assert(rend_cache); - tor_assert(desc); - tor_assert(desc_id_base32); - memset(want_desc_id, 0, sizeof(want_desc_id)); - if (base32_decode(want_desc_id, sizeof(want_desc_id), - desc_id_base32, strlen(desc_id_base32)) != 0) { - log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.", - escaped_safe_str_client(desc_id_base32)); - goto err; - } - /* Parse the descriptor. */ - if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, - &intro_size, &encoded_size, - &next_desc, desc, 0) < 0) { - log_warn(LD_REND, "Could not parse descriptor."); - goto err; - } - /* Compute service ID from public key. */ - if (rend_get_service_id(parsed->pk, service_id)<0) { - log_warn(LD_REND, "Couldn't compute service ID."); - goto err; - } - if (strcmp(rend_query->onion_address, service_id)) { - log_warn(LD_REND, "Received service descriptor for service ID %s; " - "expected descriptor for service ID %s.", - service_id, safe_str(rend_query->onion_address)); - goto err; - } - if (tor_memneq(desc_id, want_desc_id, DIGEST_LEN)) { - log_warn(LD_REND, "Received service descriptor for %s with incorrect " - "descriptor ID.", service_id); - goto err; - } - - /* Decode/decrypt introduction points. */ - if (intro_content && intro_size > 0) { - int n_intro_points; - if (rend_query->auth_type != REND_NO_AUTH && - !tor_mem_is_zero(rend_query->descriptor_cookie, - sizeof(rend_query->descriptor_cookie))) { - char *ipos_decrypted = NULL; - size_t ipos_decrypted_size; - if (rend_decrypt_introduction_points(&ipos_decrypted, - &ipos_decrypted_size, - rend_query->descriptor_cookie, - intro_content, - intro_size) < 0) { - log_warn(LD_REND, "Failed to decrypt introduction points. We are " - "probably unable to parse the encoded introduction points."); - } else { - /* Replace encrypted with decrypted introduction points. */ - log_info(LD_REND, "Successfully decrypted introduction points."); - tor_free(intro_content); - intro_content = ipos_decrypted; - intro_size = ipos_decrypted_size; - } - } - n_intro_points = rend_parse_introduction_points(parsed, intro_content, - intro_size); - if (n_intro_points <= 0) { - log_warn(LD_REND, "Failed to parse introduction points. Either the " - "service has published a corrupt descriptor or you have " - "provided invalid authorization data."); - goto err; - } else if (n_intro_points > MAX_INTRO_POINTS) { - log_warn(LD_REND, "Found too many introduction points on a hidden " - "service descriptor for %s. This is probably a (misguided) " - "attempt to improve reliability, but it could also be an " - "attempt to do a guard enumeration attack. Rejecting.", - safe_str_client(rend_query->onion_address)); - - goto err; - } - } else { - log_info(LD_REND, "Descriptor does not contain any introduction points."); - parsed->intro_nodes = smartlist_new(); - } - /* We don't need the encoded/encrypted introduction points any longer. */ - tor_free(intro_content); - /* Is descriptor too old? */ - if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) { - log_warn(LD_REND, "Service descriptor with service ID %s is too old.", - safe_str_client(service_id)); - goto err; - } - /* Is descriptor too far in the future? */ - if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) { - log_warn(LD_REND, "Service descriptor with service ID %s is too far in " - "the future.", safe_str_client(service_id)); - goto err; - } - /* Do we already have a newer descriptor? */ - tor_snprintf(key, sizeof(key), "2%s", service_id); - e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key); - if (e && e->parsed->timestamp > parsed->timestamp) { - log_info(LD_REND, "We already have a newer service descriptor for " - "service ID %s with the same desc ID and version.", - safe_str_client(service_id)); - goto okay; - } - /* Do we already have this descriptor? */ - if (e && !strcmp(desc, e->desc)) { - log_info(LD_REND,"We already have this service descriptor %s.", - safe_str_client(service_id)); - goto okay; - } - if (!e) { - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - strmap_set_lc(rend_cache, key, e); - } else { - rend_cache_decrement_allocation(rend_cache_entry_allocation(e)); - rend_service_descriptor_free(e->parsed); - tor_free(e->desc); - } - e->parsed = parsed; - e->desc = tor_malloc_zero(encoded_size + 1); - strlcpy(e->desc, desc, encoded_size + 1); - e->len = encoded_size; - rend_cache_increment_allocation(rend_cache_entry_allocation(e)); - log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.", - safe_str_client(service_id), (int)encoded_size); - return RCS_OKAY; - - okay: - retval = RCS_OKAY; - - err: - rend_service_descriptor_free(parsed); - tor_free(intro_content); - return retval; -} - /** Called when we get a rendezvous-related relay cell on circuit * <b>circ</b>. Dispatch on rendezvous relay command. */ void @@ -1324,7 +757,7 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, break; case RELAY_COMMAND_INTRODUCE2: if (origin_circ) - r = rend_service_introduce(origin_circ,payload,length); + r = rend_service_receive_introduction(origin_circ,payload,length); break; case RELAY_COMMAND_INTRODUCE_ACK: if (origin_circ) @@ -1360,7 +793,151 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint, rend_data_t * rend_data_dup(const rend_data_t *data) { + rend_data_t *data_dup; tor_assert(data); - return tor_memdup(data, sizeof(rend_data_t)); + data_dup = tor_memdup(data, sizeof(rend_data_t)); + data_dup->hsdirs_fp = smartlist_new(); + SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp, + smartlist_add(data_dup->hsdirs_fp, + tor_memdup(fp, DIGEST_LEN))); + return data_dup; +} + +/** Compute descriptor ID for each replicas and save them. A valid onion + * address must be present in the <b>rend_data</b>. + * + * Return 0 on success else -1. */ +static int +compute_desc_id(rend_data_t *rend_data) +{ + int ret = 0; + unsigned replica; + time_t now = time(NULL); + + tor_assert(rend_data); + + /* Compute descriptor ID for each replicas. */ + for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id); + replica++) { + ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica], + rend_data->onion_address, + rend_data->descriptor_cookie, + now, replica); + if (ret < 0) { + goto end; + } + } + + end: + return ret; +} + +/** Allocate and initialize a rend_data_t object for a service using the + * given arguments. Only the <b>onion_address</b> is not optional. + * + * Return a valid rend_data_t pointer. */ +rend_data_t * +rend_data_service_create(const char *onion_address, const char *pk_digest, + const uint8_t *cookie, rend_auth_type_t auth_type) +{ + rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data)); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL); + + if (pk_digest) { + memcpy(rend_data->rend_pk_digest, pk_digest, + sizeof(rend_data->rend_pk_digest)); + } + if (cookie) { + memcpy(rend_data->rend_cookie, cookie, + sizeof(rend_data->rend_cookie)); + } + + strlcpy(rend_data->onion_address, onion_address, + sizeof(rend_data->onion_address)); + rend_data->auth_type = auth_type; + /* Won't be used but still need to initialize it for rend_data dup and + * free. */ + rend_data->hsdirs_fp = smartlist_new(); + + return rend_data; +} + +/** Allocate and initialize a rend_data_t object for a client request using + * the given arguments. Either an onion address or a descriptor ID is + * needed. Both can be given but only the onion address will be used to make + * the descriptor fetch. + * + * Return a valid rend_data_t pointer or NULL on error meaning the + * descriptor IDs couldn't be computed from the given data. */ +rend_data_t * +rend_data_client_create(const char *onion_address, const char *desc_id, + const char *cookie, rend_auth_type_t auth_type) +{ + rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data)); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL || desc_id != NULL); + + if (cookie) { + memcpy(rend_data->descriptor_cookie, cookie, + sizeof(rend_data->descriptor_cookie)); + } + if (desc_id) { + memcpy(rend_data->desc_id_fetch, desc_id, + sizeof(rend_data->desc_id_fetch)); + } + if (onion_address) { + strlcpy(rend_data->onion_address, onion_address, + sizeof(rend_data->onion_address)); + if (compute_desc_id(rend_data) < 0) { + goto error; + } + } + + rend_data->auth_type = auth_type; + rend_data->hsdirs_fp = smartlist_new(); + + return rend_data; + + error: + rend_data_free(rend_data); + return NULL; +} + +/** Determine the routers that are responsible for <b>id</b> (binary) and + * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>. + * Return -1 if we're returning an empty smartlist, else return 0. + */ +int +hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, + const char *id) +{ + int start, found, n_added = 0, i; + networkstatus_t *c = networkstatus_get_latest_consensus(); + if (!c || !smartlist_len(c->routerstatus_list)) { + log_warn(LD_REND, "We don't have a consensus, so we can't perform v2 " + "rendezvous operations."); + return -1; + } + tor_assert(id); + start = networkstatus_vote_find_entry_idx(c, id, &found); + if (start == smartlist_len(c->routerstatus_list)) start = 0; + i = start; + do { + routerstatus_t *r = smartlist_get(c->routerstatus_list, i); + if (r->is_hs_dir) { + smartlist_add(responsible_dirs, r); + if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS) + return 0; + } + if (++i == smartlist_len(c->routerstatus_list)) + i = 0; + } while (i != start); + + /* Even though we don't have the desired number of hidden service + * directories, be happy if we got any. */ + return smartlist_len(responsible_dirs) ? 0 : -1; } diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 8396cc3551..d67552e405 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,10 +12,22 @@ #ifndef TOR_RENDCOMMON_H #define TOR_RENDCOMMON_H +typedef enum rend_intro_point_failure_t { + INTRO_POINT_FAILURE_GENERIC = 0, + INTRO_POINT_FAILURE_TIMEOUT = 1, + INTRO_POINT_FAILURE_UNREACHABLE = 2, +} rend_intro_point_failure_t; + /** Free all storage associated with <b>data</b> */ -static INLINE void +static inline void rend_data_free(rend_data_t *data) { + if (!data) { + return; + } + /* Cleanup the HSDir identity digest. */ + SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(data->hsdirs_fp); tor_free(data); } @@ -31,26 +43,8 @@ void rend_encoded_v2_service_descriptor_free( rend_encoded_v2_service_descriptor_t *desc); void rend_intro_point_free(rend_intro_point_t *intro); -void rend_cache_init(void); -void rend_cache_clean(time_t now); -void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove); -void rend_cache_purge(void); -void rend_cache_free_all(void); int rend_valid_service_id(const char *query); -int rend_cache_lookup_entry(const char *query, int version, - rend_cache_entry_t **entry_out); -int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc); -/** Return value from rend_cache_store_v2_desc_as_{dir,client}. */ -typedef enum { - RCS_NOTDIR = -2, /**< We're not a directory */ - RCS_BADDESC = -1, /**< This descriptor is no good. */ - RCS_OKAY = 0 /**< All worked as expected */ -} rend_cache_store_status_t; - -rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc); -rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc, - const char *desc_id_base32, - const rend_data_t *rend_query); +int rend_valid_descriptor_id(const char *query); int rend_encode_v2_descriptors(smartlist_t *descs_out, rend_service_descriptor_t *desc, time_t now, uint8_t period, rend_auth_type_t auth_type, @@ -59,11 +53,20 @@ int rend_encode_v2_descriptors(smartlist_t *descs_out, int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, const char *descriptor_cookie, time_t now, uint8_t replica); -int rend_id_is_in_interval(const char *a, const char *b, const char *c); void rend_get_descriptor_id_bytes(char *descriptor_id_out, const char *service_id, const char *secret_id_part); -size_t rend_cache_get_total_allocation(void); +int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, + const char *id); +rend_data_t *rend_data_dup(const rend_data_t *data); +rend_data_t *rend_data_client_create(const char *onion_address, + const char *desc_id, + const char *cookie, + rend_auth_type_t auth_type); +rend_data_t *rend_data_service_create(const char *onion_address, + const char *pk_digest, + const uint8_t *cookie, + rend_auth_type_t auth_type); #endif diff --git a/src/or/rendmid.c b/src/or/rendmid.c index 2451acb514..a33ad92966 100644 --- a/src/or/rendmid.c +++ b/src/or/rendmid.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -80,7 +80,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, goto err; } - /* The request is valid. First, compute the hash of Bob's PK.*/ + /* The request is valid. First, compute the hash of the service's PK.*/ if (crypto_pk_get_digest(pk, pk_digest)<0) { log_warn(LD_BUG, "Internal error: couldn't hash public key."); goto err; @@ -178,7 +178,8 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, base32_encode(serviceid, REND_SERVICE_ID_LEN_BASE32+1, (char*)request, REND_SERVICE_ID_LEN); - /* The first 20 bytes are all we look at: they have a hash of Bob's PK. */ + /* The first 20 bytes are all we look at: they have a hash of the service's + * PK. */ intro_circ = circuit_get_intro_point((const uint8_t*)request); if (!intro_circ) { log_info(LD_REND, @@ -202,7 +203,7 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, "Unable to send INTRODUCE2 cell to Tor client."); goto err; } - /* And send an ack down Alice's circuit. Empty body means succeeded. */ + /* And send an ack down the client's circuit. Empty body means succeeded. */ if (relay_send_command_from_edge(0,TO_CIRCUIT(circ), RELAY_COMMAND_INTRODUCE_ACK, NULL,0,NULL)) { @@ -337,7 +338,7 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, circ->circuit_carries_hs_traffic_stats = 1; } - /* Send the RENDEZVOUS2 cell to Alice. */ + /* Send the RENDEZVOUS2 cell to the client. */ if (relay_send_command_from_edge(0, TO_CIRCUIT(rend_circ), RELAY_COMMAND_RENDEZVOUS2, (char*)(request+REND_COOKIE_LEN), diff --git a/src/or/rendmid.h b/src/or/rendmid.h index 6bd691a740..10d1287085 100644 --- a/src/or/rendmid.h +++ b/src/or/rendmid.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 111b369b1c..b81a01c568 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,6 +15,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "config.h" +#include "control.h" #include "directory.h" #include "main.h" #include "networkstatus.h" @@ -30,21 +31,29 @@ #include "routerparse.h" #include "routerset.h" +struct rend_service_t; static origin_circuit_t *find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest); static rend_intro_point_t *find_intro_point(origin_circuit_t *circ); +static rend_intro_point_t *find_expiring_intro_point( + struct rend_service_t *service, origin_circuit_t *circ); static extend_info_t *find_rp_for_intro( const rend_intro_cell_t *intro, - uint8_t *need_free_out, char **err_msg_out); + char **err_msg_out); static int intro_point_accepted_intro_count(rend_intro_point_t *intro); static int intro_point_should_expire_now(rend_intro_point_t *intro, time_t now); -struct rend_service_t; +static int rend_service_derive_key_digests(struct rend_service_t *s); static int rend_service_load_keys(struct rend_service_t *s); static int rend_service_load_auth_keys(struct rend_service_t *s, const char *hfname); +static struct rend_service_t *rend_service_get_by_pk_digest( + const char* digest); +static struct rend_service_t *rend_service_get_by_service_id(const char *id); +static const char *rend_service_escaped_dir( + const struct rend_service_t *s); static ssize_t rend_service_parse_intro_for_v0_or_v1( rend_intro_cell_t *intro, @@ -65,7 +74,7 @@ static ssize_t rend_service_parse_intro_for_v3( /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. */ -typedef struct rend_service_port_config_t { +struct rend_service_port_config_s { /* The incoming HS virtual port we're mapping */ uint16_t virtual_port; /* Is this an AF_UNIX port? */ @@ -76,12 +85,15 @@ typedef struct rend_service_port_config_t { tor_addr_t real_addr; /* The socket path to connect to, if is_unix_addr */ char unix_addr[FLEXIBLE_ARRAY_MEMBER]; -} rend_service_port_config_t; +}; /** Try to maintain this many intro points per service by default. */ #define NUM_INTRO_POINTS_DEFAULT 3 -/** Maintain no more than this many intro points per hidden service. */ +/** Maximum number of intro points per service. */ #define NUM_INTRO_POINTS_MAX 10 +/** Number of extra intro points we launch if our set of intro nodes is + * empty. See proposal 155, section 4. */ +#define NUM_INTRO_POINTS_EXTRA 2 /** If we can't build our intro circuits, don't retry for this long. */ #define INTRO_CIRC_RETRY_PERIOD (60*5) @@ -95,14 +107,11 @@ typedef struct rend_service_port_config_t { * rendezvous point before giving up? */ #define MAX_REND_TIMEOUT 30 -/** How many seconds should we wait for new HS descriptors to reach - * our clients before we close an expiring intro point? */ -#define INTRO_POINT_EXPIRATION_GRACE_PERIOD (5*60) - /** Represents a single hidden service running at this OP. */ typedef struct rend_service_t { /* Fields specified in config file */ - char *directory; /**< where in the filesystem it stores it */ + char *directory; /**< where in the filesystem it stores it. Will be NULL if + * this service is ephemeral. */ int dir_group_readable; /**< if 1, allow group read permissions on directory */ smartlist_t *ports; /**< List of rend_service_port_config_t */ @@ -118,6 +127,10 @@ typedef struct rend_service_t { char pk_digest[DIGEST_LEN]; /**< Hash of permanent hidden-service key. */ smartlist_t *intro_nodes; /**< List of rend_intro_point_t's we have, * or are trying to establish. */ + /** List of rend_intro_point_t that are expiring. They are removed once + * the new descriptor is successfully uploaded. A node in this list CAN + * NOT appear in the intro_nodes list. */ + smartlist_t *expiring_nodes; time_t intro_period_started; /**< Start of the current period to build * introduction points. */ int n_intro_circuits_launched; /**< Count of intro circuits we have @@ -139,8 +152,23 @@ typedef struct rend_service_t { /** If true, we don't close circuits for making requests to unsupported * ports. */ int allow_unknown_ports; + /** The maximum number of simultanious streams-per-circuit that are allowed + * to be established, or 0 if no limit is set. + */ + int max_streams_per_circuit; + /** If true, we close circuits that exceed the max_streams_per_circuit + * limit. */ + int max_streams_close_circuit; } rend_service_t; +/** Returns a escaped string representation of the service, <b>s</b>. + */ +static const char * +rend_service_escaped_dir(const struct rend_service_t *s) +{ + return (s->directory) ? escaped(s->directory) : "[EPHEMERAL]"; +} + /** A list of rend_service_t's for services run on this OP. */ static smartlist_t *rend_service_list = NULL; @@ -154,17 +182,6 @@ num_rend_services(void) return smartlist_len(rend_service_list); } -/** Return a string identifying <b>service</b>, suitable for use in a - * log message. The result does not need to be freed, but may be - * overwritten by the next call to this function. */ -static const char * -rend_service_describe_for_log(rend_service_t *service) -{ - /* XXX024 Use this function throughout rendservice.c. */ - /* XXX024 Return a more useful description? */ - return safe_str_client(service->service_id); -} - /** Helper: free storage held by a single service authorized client entry. */ static void rend_authorized_client_free(rend_authorized_client_t *client) @@ -173,7 +190,7 @@ rend_authorized_client_free(rend_authorized_client_t *client) return; if (client->client_key) crypto_pk_free(client->client_key); - tor_strclear(client->client_name); + memwipe(client->client_name, 0, strlen(client->client_name)); tor_free(client->client_name); memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie)); tor_free(client); @@ -195,7 +212,8 @@ rend_service_free(rend_service_t *service) return; tor_free(service->directory); - SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p)); + SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p, + rend_service_port_config_free(p)); smartlist_free(service->ports); if (service->private_key) crypto_pk_free(service->private_key); @@ -204,6 +222,11 @@ rend_service_free(rend_service_t *service) rend_intro_point_free(intro);); smartlist_free(service->intro_nodes); } + if (service->expiring_nodes) { + SMARTLIST_FOREACH(service->expiring_nodes, rend_intro_point_t *, intro, + rend_intro_point_free(intro);); + smartlist_free(service->expiring_nodes); + } rend_service_descriptor_free(service->desc); if (service->clients) { @@ -232,29 +255,49 @@ rend_service_free_all(void) } /** Validate <b>service</b> and add it to rend_service_list if possible. + * Return 0 on success. On failure, free <b>service</b> and return -1. */ -static void +static int rend_add_service(rend_service_t *service) { int i; rend_service_port_config_t *p; service->intro_nodes = smartlist_new(); + service->expiring_nodes = smartlist_new(); + + if (service->max_streams_per_circuit < 0) { + log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max " + "streams per circuit; ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } + + if (service->max_streams_close_circuit < 0 || + service->max_streams_close_circuit > 1) { + log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid " + "max streams handling; ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } if (service->auth_type != REND_NO_AUTH && smartlist_len(service->clients) == 0) { log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no " "clients; ignoring.", - escaped(service->directory)); + rend_service_escaped_dir(service)); rend_service_free(service); - return; + return -1; } if (!smartlist_len(service->ports)) { log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; " "ignoring.", - escaped(service->directory)); + rend_service_escaped_dir(service)); rend_service_free(service); + return -1; } else { int dupe = 0; /* XXX This duplicate check has two problems: @@ -272,14 +315,17 @@ rend_add_service(rend_service_t *service) * lock file. But this is enough to detect a simple mistake that * at least one person has actually made. */ - SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, - dupe = dupe || - !strcmp(ptr->directory, service->directory)); - if (dupe) { - log_warn(LD_REND, "Another hidden service is already configured for " - "directory %s, ignoring.", service->directory); - rend_service_free(service); - return; + if (service->directory != NULL) { /* Skip dupe for ephemeral services. */ + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, + dupe = dupe || + !strcmp(ptr->directory, service->directory)); + if (dupe) { + log_warn(LD_REND, "Another hidden service is already configured for " + "directory %s, ignoring.", + rend_service_escaped_dir(service)); + rend_service_free(service); + return -1; + } } smartlist_add(rend_service_list, service); log_debug(LD_REND,"Configuring service with directory \"%s\"", @@ -305,7 +351,9 @@ rend_add_service(rend_service_t *service) #endif /* defined(HAVE_SYS_UN_H) */ } } + return 0; } + /* NOTREACHED */ } /** Return a new rend_service_port_config_t with its path set to @@ -324,15 +372,17 @@ rend_service_port_config_new(const char *socket_path) return conf; } -/** Parses a real-port to virtual-port mapping and returns a new - * rend_service_port_config_t. +/** Parses a real-port to virtual-port mapping separated by the provided + * separator and returns a new rend_service_port_config_t, or NULL and an + * optional error string on failure. * - * The format is: VirtualPort (IP|RealPort|IP:RealPort|'socket':path)? + * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)? * * IP defaults to 127.0.0.1; RealPort defaults to VirtualPort. */ -static rend_service_port_config_t * -parse_port_config(const char *string) +rend_service_port_config_t * +rend_service_parse_port_config(const char *string, const char *sep, + char **err_msg_out) { smartlist_t *sl; int virtport; @@ -343,19 +393,24 @@ parse_port_config(const char *string) rend_service_port_config_t *result = NULL; unsigned int is_unix_addr = 0; char *socket_path = NULL; + char *err_msg = NULL; sl = smartlist_new(); - smartlist_split_string(sl, string, " ", + smartlist_split_string(sl, string, sep, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) { - log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration."); + if (err_msg_out) + err_msg = tor_strdup("Bad syntax in hidden service port configuration."); + goto err; } virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL); if (!virtport) { - log_warn(LD_CONFIG, "Missing or invalid port %s in hidden service port " - "configuration", escaped(smartlist_get(sl,0))); + if (err_msg_out) + tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " + "port configuration", escaped(smartlist_get(sl,0))); + goto err; } @@ -369,10 +424,11 @@ parse_port_config(const char *string) addrport = smartlist_get(sl,1); ret = config_parse_unix_port(addrport, &socket_path); if (ret < 0 && ret != -ENOENT) { - if (ret == -EINVAL) { - log_warn(LD_CONFIG, - "Empty socket path in hidden service port configuration."); - } + if (ret == -EINVAL) + if (err_msg_out) + err_msg = tor_strdup("Empty socket path in hidden service port " + "configuration."); + goto err; } if (socket_path) { @@ -380,8 +436,10 @@ parse_port_config(const char *string) } else if (strchr(addrport, ':') || strchr(addrport, '.')) { /* else try it as an IP:port pair if it has a : or . in it */ if (tor_addr_port_lookup(addrport, &addr, &p)<0) { - log_warn(LD_CONFIG,"Unparseable address in hidden service port " - "configuration."); + if (err_msg_out) + err_msg = tor_strdup("Unparseable address in hidden service port " + "configuration."); + goto err; } realport = p?p:virtport; @@ -389,8 +447,11 @@ parse_port_config(const char *string) /* No addr:port, no addr -- must be port. */ realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL); if (!realport) { - log_warn(LD_CONFIG,"Unparseable or out-of-range port %s in hidden " - "service port configuration.", escaped(addrport)); + if (err_msg_out) + tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " + "hidden service port configuration.", + escaped(addrport)); + goto err; } tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */ @@ -408,6 +469,7 @@ parse_port_config(const char *string) } err: + if (err_msg_out) *err_msg_out = err_msg; SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); if (socket_path) tor_free(socket_path); @@ -415,6 +477,13 @@ parse_port_config(const char *string) return result; } +/** Release all storage held in a rend_service_port_config_t. */ +void +rend_service_port_config_free(rend_service_port_config_t *p) +{ + tor_free(p); +} + /** Set up rend_service_list, based on the values of HiddenServiceDir and * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on * failure. (If <b>validate_only</b> is set, parse, warn and return as @@ -439,113 +508,161 @@ rend_config_services(const or_options_t *options, int validate_only) if (service) { /* register the one we just finished parsing */ if (validate_only) rend_service_free(service); - else - rend_add_service(service); - } - service = tor_malloc_zero(sizeof(rend_service_t)); - service->directory = tor_strdup(line->value); - service->ports = smartlist_new(); - service->intro_period_started = time(NULL); - service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; - continue; - } - if (!service) { - log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", - line->key); - rend_service_free(service); - return -1; - } - if (!strcasecmp(line->key, "HiddenServicePort")) { - portcfg = parse_port_config(line->value); - if (!portcfg) { - rend_service_free(service); - return -1; - } - smartlist_add(service->ports, portcfg); - } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { - service->allow_unknown_ports = (int)tor_parse_long(line->value, - 10, 0, 1, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, - "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", - line->value); - rend_service_free(service); - return -1; - } - log_info(LD_CONFIG, - "HiddenServiceAllowUnknownPorts=%d for %s", - (int)service->allow_unknown_ports, service->directory); - } else if (!strcasecmp(line->key, - "HiddenServiceDirGroupReadable")) { - service->dir_group_readable = (int)tor_parse_long(line->value, - 10, 0, 1, &ok, NULL); - if (!ok) { - log_warn(LD_CONFIG, - "HiddenServiceDirGroupReadable should be 0 or 1, not %s", - line->value); - rend_service_free(service); - return -1; - } - log_info(LD_CONFIG, - "HiddenServiceDirGroupReadable=%d for %s", - service->dir_group_readable, service->directory); - } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) { - /* Parse auth type and comma-separated list of client names and add a - * rend_authorized_client_t for each client to the service's list - * of authorized clients. */ - smartlist_t *type_names_split, *clients; - const char *authname; - int num_clients; - if (service->auth_type != REND_NO_AUTH) { - log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " - "lines for a single service."); - rend_service_free(service); - return -1; - } - type_names_split = smartlist_new(); - smartlist_split_string(type_names_split, line->value, " ", 0, 2); - if (smartlist_len(type_names_split) < 1) { - log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " - "should have been prevented when parsing the " - "configuration."); - smartlist_free(type_names_split); - rend_service_free(service); - return -1; - } - authname = smartlist_get(type_names_split, 0); - if (!strcasecmp(authname, "basic")) { - service->auth_type = REND_BASIC_AUTH; - } else if (!strcasecmp(authname, "stealth")) { - service->auth_type = REND_STEALTH_AUTH; - } else { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " - "are recognized.", - (char *) smartlist_get(type_names_split, 0)); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - rend_service_free(service); - return -1; - } - service->clients = smartlist_new(); - if (smartlist_len(type_names_split) < 2) { - log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " - "auth-type '%s', but no client names.", - service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - continue; - } - clients = smartlist_new(); - smartlist_split_string(clients, smartlist_get(type_names_split, 1), - ",", SPLIT_SKIP_SPACE, 0); - SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); - smartlist_free(type_names_split); - /* Remove duplicate client names. */ - num_clients = smartlist_len(clients); - smartlist_sort_strings(clients); - smartlist_uniq_strings(clients); - if (smartlist_len(clients) < num_clients) { + else + rend_add_service(service); + } + service = tor_malloc_zero(sizeof(rend_service_t)); + service->directory = tor_strdup(line->value); + service->ports = smartlist_new(); + service->intro_period_started = time(NULL); + service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; + continue; + } + if (!service) { + log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive", + line->key); + rend_service_free(service); + return -1; + } + if (!strcasecmp(line->key, "HiddenServicePort")) { + char *err_msg = NULL; + portcfg = rend_service_parse_port_config(line->value, " ", &err_msg); + if (!portcfg) { + if (err_msg) + log_warn(LD_CONFIG, "%s", err_msg); + tor_free(err_msg); + rend_service_free(service); + return -1; + } + tor_assert(!err_msg); + smartlist_add(service->ports, portcfg); + } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) { + service->allow_unknown_ports = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceAllowUnknownPorts=%d for %s", + (int)service->allow_unknown_ports, service->directory); + } else if (!strcasecmp(line->key, + "HiddenServiceDirGroupReadable")) { + service->dir_group_readable = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceDirGroupReadable should be 0 or 1, not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceDirGroupReadable=%d for %s", + service->dir_group_readable, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) { + service->max_streams_per_circuit = (int)tor_parse_long(line->value, + 10, 0, 65535, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreams should be between 0 and %d, not %s", + 65535, line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreams=%d for %s", + service->max_streams_per_circuit, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) { + service->max_streams_close_circuit = (int)tor_parse_long(line->value, + 10, 0, 1, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, " + "not %s", + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, + "HiddenServiceMaxStreamsCloseCircuit=%d for %s", + (int)service->max_streams_close_circuit, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) { + service->n_intro_points_wanted = + (unsigned int) tor_parse_long(line->value, 10, + NUM_INTRO_POINTS_DEFAULT, + NUM_INTRO_POINTS_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_CONFIG, + "HiddenServiceNumIntroductionPoints " + "should be between %d and %d, not %s", + NUM_INTRO_POINTS_DEFAULT, NUM_INTRO_POINTS_MAX, + line->value); + rend_service_free(service); + return -1; + } + log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s", + service->n_intro_points_wanted, service->directory); + } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) { + /* Parse auth type and comma-separated list of client names and add a + * rend_authorized_client_t for each client to the service's list + * of authorized clients. */ + smartlist_t *type_names_split, *clients; + const char *authname; + int num_clients; + if (service->auth_type != REND_NO_AUTH) { + log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient " + "lines for a single service."); + rend_service_free(service); + return -1; + } + type_names_split = smartlist_new(); + smartlist_split_string(type_names_split, line->value, " ", 0, 2); + if (smartlist_len(type_names_split) < 1) { + log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This " + "should have been prevented when parsing the " + "configuration."); + smartlist_free(type_names_split); + rend_service_free(service); + return -1; + } + authname = smartlist_get(type_names_split, 0); + if (!strcasecmp(authname, "basic")) { + service->auth_type = REND_BASIC_AUTH; + } else if (!strcasecmp(authname, "stealth")) { + service->auth_type = REND_STEALTH_AUTH; + } else { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "unrecognized auth-type '%s'. Only 'basic' or 'stealth' " + "are recognized.", + (char *) smartlist_get(type_names_split, 0)); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + rend_service_free(service); + return -1; + } + service->clients = smartlist_new(); + if (smartlist_len(type_names_split) < 2) { + log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains " + "auth-type '%s', but no client names.", + service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth"); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + continue; + } + clients = smartlist_new(); + smartlist_split_string(clients, smartlist_get(type_names_split, 1), + ",", SPLIT_SKIP_SPACE, 0); + SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp)); + smartlist_free(type_names_split); + /* Remove duplicate client names. */ + num_clients = smartlist_len(clients); + smartlist_sort_strings(clients); + smartlist_uniq_strings(clients); + if (smartlist_len(clients) < num_clients) { log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d " "duplicate client name(s); removing.", num_clients - smartlist_len(clients)); @@ -632,14 +749,39 @@ rend_config_services(const or_options_t *options, int validate_only) if (old_service_list && !validate_only) { smartlist_t *surviving_services = smartlist_new(); + /* Preserve the existing ephemeral services. + * + * This is the ephemeral service equivalent of the "Copy introduction + * points to new services" block, except there's no copy required since + * the service structure isn't regenerated. + * + * After this is done, all ephemeral services will be: + * * Removed from old_service_list, so the equivalent non-ephemeral code + * will not attempt to preserve them. + * * Added to the new rend_service_list (that previously only had the + * services listed in the configuration). + * * Added to surviving_services, which is the list of services that + * will NOT have their intro point closed. + */ + SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, { + if (!old->directory) { + SMARTLIST_DEL_CURRENT(old_service_list, old); + smartlist_add(surviving_services, old); + smartlist_add(rend_service_list, old); + } + }); + /* Copy introduction points to new services. */ /* XXXX This is O(n^2), but it's only called on reconfigure, so it's * probably ok? */ SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) { SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { - if (!strcmp(old->directory, new->directory)) { + if (new->directory && old->directory && + !strcmp(old->directory, new->directory)) { smartlist_add_all(new->intro_nodes, old->intro_nodes); smartlist_clear(old->intro_nodes); + smartlist_add_all(new->expiring_nodes, old->expiring_nodes); + smartlist_clear(old->expiring_nodes); smartlist_add(surviving_services, old); break; } @@ -685,6 +827,124 @@ rend_config_services(const or_options_t *options, int validate_only) return 0; } +/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with + * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit, + * and circuit closure on max streams being exceeded set by + * <b>max_streams_close_circuit</b>. + * + * Regardless of sucess/failure, callers should not touch pk/ports after + * calling this routine, and may assume that correct cleanup has been done + * on failure. + * + * Return an appropriate rend_service_add_ephemeral_status_t. + */ +rend_service_add_ephemeral_status_t +rend_service_add_ephemeral(crypto_pk_t *pk, + smartlist_t *ports, + int max_streams_per_circuit, + int max_streams_close_circuit, + char **service_id_out) +{ + *service_id_out = NULL; + /* Allocate the service structure, and initialize the key, and key derived + * parameters. + */ + rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t)); + s->directory = NULL; /* This indicates the service is ephemeral. */ + s->private_key = pk; + s->auth_type = REND_NO_AUTH; + s->ports = ports; + s->intro_period_started = time(NULL); + s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT; + s->max_streams_per_circuit = max_streams_per_circuit; + s->max_streams_close_circuit = max_streams_close_circuit; + if (rend_service_derive_key_digests(s) < 0) { + rend_service_free(s); + return RSAE_BADPRIVKEY; + } + + if (!s->ports || smartlist_len(s->ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified."); + rend_service_free(s); + return RSAE_BADVIRTPORT; + } + + /* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but + * it's not, see #14828. + */ + if (rend_service_get_by_pk_digest(s->pk_digest)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing service."); + rend_service_free(s); + return RSAE_ADDREXISTS; + } + if (rend_service_get_by_service_id(s->service_id)) { + log_warn(LD_CONFIG, "Onion Service id collides with an existing service."); + rend_service_free(s); + return RSAE_ADDREXISTS; + } + + /* Initialize the service. */ + if (rend_add_service(s)) { + return RSAE_INTERNAL; + } + *service_id_out = tor_strdup(s->service_id); + + log_debug(LD_CONFIG, "Added ephemeral Onion Service: %s", s->service_id); + return RSAE_OKAY; +} + +/** Remove the ephemeral service <b>service_id</b> if possible. Returns 0 on + * success, and -1 on failure. + */ +int +rend_service_del_ephemeral(const char *service_id) +{ + rend_service_t *s; + if (!rend_valid_service_id(service_id)) { + log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal."); + return -1; + } + if ((s = rend_service_get_by_service_id(service_id)) == NULL) { + log_warn(LD_CONFIG, "Requested non-existent Onion Service id for " + "removal."); + return -1; + } + if (s->directory) { + log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal."); + return -1; + } + + /* Kill the intro point circuit for the Onion Service, and remove it from + * the list. Closing existing connections is the application's problem. + * + * XXX: As with the comment in rend_config_services(), a nice abstraction + * would be ideal here, but for now just duplicate the code. + */ + SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { + if (!circ->marked_for_close && + circ->state == CIRCUIT_STATE_OPEN && + (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + tor_assert(oc->rend_data); + if (!tor_memeq(s->pk_digest, oc->rend_data->rend_pk_digest, DIGEST_LEN)) + continue; + log_debug(LD_REND, "Closing intro point %s for service %s.", + safe_str_client(extend_info_describe( + oc->build_state->chosen_exit)), + oc->rend_data->onion_address); + circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); + } + } SMARTLIST_FOREACH_END(circ); + smartlist_remove(rend_service_list, s); + rend_service_free(s); + + log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id); + + return 0; +} + /** Replace the old value of <b>service</b>-\>desc with one that reflects * the other fields in service. */ @@ -713,11 +973,6 @@ rend_service_update_descriptor(rend_service_t *service) /* This intro point won't be listed in the descriptor... */ intro_svc->listed_in_last_desc = 0; - if (intro_svc->time_expiring != -1) { - /* This intro point is expiring. Don't list it. */ - continue; - } - circ = find_intro_circuit(intro_svc, service->pk_digest); if (!circ || circ->base_.purpose != CIRCUIT_PURPOSE_S_INTRO) { /* This intro point's circuit isn't finished yet. Don't list it. */ @@ -769,6 +1024,7 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s) { tor_assert(lst); tor_assert(s); + tor_assert(s->directory); smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key", s->directory); smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname", @@ -787,11 +1043,31 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst, if (!rend_service_list) return; SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) { - rend_service_add_filenames_to_list(open_lst, s); - smartlist_add(stat_lst, tor_strdup(s->directory)); + if (s->directory) { + rend_service_add_filenames_to_list(open_lst, s); + smartlist_add(stat_lst, tor_strdup(s->directory)); + } } SMARTLIST_FOREACH_END(s); } +/** Derive all rend_service_t internal material based on the service's key. + * Returns 0 on sucess, -1 on failure. + */ +static int +rend_service_derive_key_digests(struct rend_service_t *s) +{ + if (rend_get_service_id(s->private_key, s->service_id)<0) { + log_warn(LD_BUG, "Internal error: couldn't encode service ID."); + return -1; + } + if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { + log_warn(LD_BUG, "Couldn't compute hash of public key."); + return -1; + } + + return 0; +} + /** Load and/or generate private keys for the hidden service <b>s</b>, * possibly including keys for client authorization. Return 0 on success, -1 * on failure. */ @@ -830,15 +1106,10 @@ rend_service_load_keys(rend_service_t *s) if (!s->private_key) return -1; - /* Create service file */ - if (rend_get_service_id(s->private_key, s->service_id)<0) { - log_warn(LD_BUG, "Internal error: couldn't encode service ID."); - return -1; - } - if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) { - log_warn(LD_BUG, "Couldn't compute hash of public key."); + if (rend_service_derive_key_digests(s) < 0) return -1; - } + + /* Create service file */ if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) >= sizeof(fname)) { @@ -941,7 +1212,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) } if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, client->descriptor_cookie, - REND_DESC_COOKIE_LEN) < 0) { + REND_DESC_COOKIE_LEN, 0) < 0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); goto err; } @@ -968,7 +1239,6 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) client->client_key = prkey; } /* Add entry to client_keys file. */ - desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */ written = tor_snprintf(buf, sizeof(buf), "client-name %s\ndescriptor-cookie %s\n", client->client_name, desc_cook_out); @@ -1023,12 +1293,11 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) ((int)s->auth_type - 1) << 4; if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1, extended_desc_cookie, - REND_DESC_COOKIE_LEN+1) < 0) { + REND_DESC_COOKIE_LEN+1, 0) < 0) { log_warn(LD_BUG, "Could not base64-encode descriptor cookie."); goto err; } - desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and - newline. */ + desc_cook_out[strlen(desc_cook_out)-2] = '\0'; /* Remove A=. */ tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n", service_id, desc_cook_out, client->client_name); } @@ -1052,7 +1321,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) abort_writing_to_file(open_hfile); done: if (client_keys_str) { - tor_strclear(client_keys_str); + memwipe(client_keys_str, 0, strlen(client_keys_str)); tor_free(client_keys_str); } strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); @@ -1080,6 +1349,20 @@ rend_service_get_by_pk_digest(const char* digest) return NULL; } +/** Return the service whose service id is <b>id</b>, or NULL if no such + * service exists. + */ +static struct rend_service_t * +rend_service_get_by_service_id(const char *id) +{ + tor_assert(strlen(id) == REND_SERVICE_ID_LEN_BASE32); + SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, { + if (tor_memeq(s->service_id, id, REND_SERVICE_ID_LEN_BASE32)) + return s; + }); + return NULL; +} + /** Return 1 if any virtual port in <b>service</b> wants a circuit * to have good uptime. Else return 0. */ @@ -1133,7 +1416,7 @@ rend_check_authorization(rend_service_t *service, if (!auth_client) { char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64]; base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64), - descriptor_cookie, REND_DESC_COOKIE_LEN); + descriptor_cookie, REND_DESC_COOKIE_LEN, 0); log_info(LD_REND, "No authorization found for descriptor cookie '%s'! " "Dropping cell!", descriptor_cookie_base64); @@ -1146,106 +1429,6 @@ rend_check_authorization(rend_service_t *service, return 1; } -/** Called when <b>intro</b> will soon be removed from - * <b>service</b>'s list of intro points. */ -static void -rend_service_note_removing_intro_point(rend_service_t *service, - rend_intro_point_t *intro) -{ - time_t now = time(NULL); - - /* Don't process an intro point twice here. */ - if (intro->rend_service_note_removing_intro_point_called) { - return; - } else { - intro->rend_service_note_removing_intro_point_called = 1; - } - - /* Update service->n_intro_points_wanted based on how long intro - * lasted and how many introductions it handled. */ - if (intro->time_published == -1) { - /* This intro point was never used. Don't change - * n_intro_points_wanted. */ - } else { - /* We want to increase the number of introduction points service - * operates if intro was heavily used, or decrease the number of - * intro points if intro was lightly used. - * - * We consider an intro point's target 'usage' to be - * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in - * INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's - * fraction of target usage, we divide the fraction of - * _LIFETIME_INTRODUCTIONS introductions that it has handled by - * the fraction of _LIFETIME_MIN_SECONDS for which it existed. - * - * Then we multiply that fraction of desired usage by a fudge - * factor of 1.5, to decide how many new introduction points - * should ideally replace intro (which is now closed or soon to be - * closed). In theory, assuming that introduction load is - * distributed equally across all intro points and ignoring the - * fact that different intro points are established and closed at - * different times, that number of intro points should bring all - * of our intro points exactly to our target usage. - * - * Then we clamp that number to a number of intro points we might - * be willing to replace this intro point with and turn it into an - * integer. then we clamp it again to the number of new intro - * points we could establish now, then we adjust - * service->n_intro_points_wanted and let rend_services_introduce - * create the new intro points we want (if any). - */ - const double intro_point_usage = - intro_point_accepted_intro_count(intro) / - (double)(now - intro->time_published); - const double intro_point_target_usage = - INTRO_POINT_LIFETIME_INTRODUCTIONS / - (double)INTRO_POINT_LIFETIME_MIN_SECONDS; - const double fractional_n_intro_points_wanted_to_replace_this_one = - (1.5 * (intro_point_usage / intro_point_target_usage)); - unsigned int n_intro_points_wanted_to_replace_this_one; - unsigned int n_intro_points_wanted_now; - unsigned int n_intro_points_really_wanted_now; - int n_intro_points_really_replacing_this_one; - - if (fractional_n_intro_points_wanted_to_replace_this_one > - NUM_INTRO_POINTS_MAX) { - n_intro_points_wanted_to_replace_this_one = NUM_INTRO_POINTS_MAX; - } else if (fractional_n_intro_points_wanted_to_replace_this_one < 0) { - n_intro_points_wanted_to_replace_this_one = 0; - } else { - n_intro_points_wanted_to_replace_this_one = (unsigned) - fractional_n_intro_points_wanted_to_replace_this_one; - } - - n_intro_points_wanted_now = - service->n_intro_points_wanted + - n_intro_points_wanted_to_replace_this_one - 1; - - if (n_intro_points_wanted_now < NUM_INTRO_POINTS_DEFAULT) { - /* XXXX This should be NUM_INTRO_POINTS_MIN instead. Perhaps - * another use of NUM_INTRO_POINTS_DEFAULT should be, too. */ - n_intro_points_really_wanted_now = NUM_INTRO_POINTS_DEFAULT; - } else if (n_intro_points_wanted_now > NUM_INTRO_POINTS_MAX) { - n_intro_points_really_wanted_now = NUM_INTRO_POINTS_MAX; - } else { - n_intro_points_really_wanted_now = n_intro_points_wanted_now; - } - - n_intro_points_really_replacing_this_one = - n_intro_points_really_wanted_now - service->n_intro_points_wanted + 1; - - log_info(LD_REND, "Replacing closing intro point for service %s " - "with %d new intro points (wanted %g replacements); " - "service will now try to have %u intro points", - rend_service_describe_for_log(service), - n_intro_points_really_replacing_this_one, - fractional_n_intro_points_wanted_to_replace_this_one, - n_intro_points_really_wanted_now); - - service->n_intro_points_wanted = n_intro_points_really_wanted_now; - } -} - /****** * Handle cells ******/ @@ -1254,13 +1437,15 @@ rend_service_note_removing_intro_point(rend_service_t *service, * rendezvous point. */ int -rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, - size_t request_len) +rend_service_receive_introduction(origin_circuit_t *circuit, + const uint8_t *request, + size_t request_len) { /* Global status stuff */ int status = 0, result; const or_options_t *options = get_options(); char *err_msg = NULL; + int err_msg_severity = LOG_WARN; const char *stage_descr = NULL; int reason = END_CIRC_REASON_TORPROTOCOL; /* Service/circuit/key stuff we can learn before parsing */ @@ -1272,13 +1457,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, rend_intro_cell_t *parsed_req = NULL; /* Rendezvous point */ extend_info_t *rp = NULL; - /* - * We need to look up and construct the extend_info_t for v0 and v1, - * but all the info is in the cell and it's constructed by the parser - * for v2 and v3, so freeing it would be a double-free. Use this to - * keep track of whether we should free it. - */ - uint8_t need_rp_free = 0; /* XXX not handled yet */ char buf[RELAY_PAYLOAD_SIZE]; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */ @@ -1322,12 +1500,15 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, intro_point = find_intro_point(circuit); if (intro_point == NULL) { - log_warn(LD_BUG, - "Internal error: Got an INTRODUCE2 cell on an " - "intro circ (for service %s) with no corresponding " - "rend_intro_point_t.", - escaped(serviceid)); - goto err; + intro_point = find_expiring_intro_point(service, circuit); + if (intro_point == NULL) { + log_warn(LD_BUG, + "Internal error: Got an INTRODUCE2 cell on an " + "intro circ (for service %s) with no corresponding " + "rend_intro_point_t.", + escaped(serviceid)); + goto err; + } } log_info(LD_REND, "Received INTRODUCE2 cell for service %s on circ %u.", @@ -1351,17 +1532,6 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, tor_free(err_msg); } - stage_descr = "early validation"; - /* Early validation of pk/ciphertext part */ - result = rend_service_validate_intro_early(parsed_req, &err_msg); - if (result < 0) { - goto log_error; - } else if (err_msg) { - log_info(LD_REND, "%s on circ %u.", err_msg, - (unsigned)circuit->base_.n_circ_id); - tor_free(err_msg); - } - /* make sure service replay caches are present */ if (!service->accepted_intro_dh_parts) { service->accepted_intro_dh_parts = @@ -1426,9 +1596,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, ++(intro_point->accepted_introduce2_count); /* Find the rendezvous point */ - rp = find_rp_for_intro(parsed_req, &need_rp_free, &err_msg); - if (!rp) + rp = find_rp_for_intro(parsed_req, &err_msg); + if (!rp) { + err_msg_severity = LOG_PROTOCOL_WARN; goto log_error; + } /* Check if we'd refuse to talk to this router */ if (options->StrictNodes && @@ -1507,7 +1679,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, /* help predict this next time */ rep_hist_note_used_internal(now, circ_needs_uptime, 1); - /* Launch a circuit to alice's chosen rendezvous point. + /* Launch a circuit to the client's chosen rendezvous point. */ for (i=0;i<MAX_REND_FAILURES;i++) { int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; @@ -1533,13 +1705,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, hexcookie, serviceid); tor_assert(launched->build_state); /* Fill in the circuit's state. */ - launched->rend_data = tor_malloc_zero(sizeof(rend_data_t)); - memcpy(launched->rend_data->rend_pk_digest, - circuit->rend_data->rend_pk_digest, - DIGEST_LEN); - memcpy(launched->rend_data->rend_cookie, parsed_req->rc, REND_COOKIE_LEN); - strlcpy(launched->rend_data->onion_address, service->service_id, - sizeof(launched->rend_data->onion_address)); + + launched->rend_data = + rend_data_service_create(service->service_id, + circuit->rend_data->rend_pk_digest, + parsed_req->rc, service->auth_type); launched->build_state->service_pending_final_cpath_ref = tor_malloc_zero(sizeof(crypt_path_reference_t)); @@ -1568,7 +1738,7 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, } } - log_warn(LD_REND, "%s on circ %u", err_msg, + log_fn(err_msg_severity, LD_REND, "%s on circ %u", err_msg, (unsigned)circuit->base_.n_circ_id); err: status = -1; @@ -1587,27 +1757,25 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, /* Free the parsed cell */ rend_service_free_intro(parsed_req); - /* Free rp if we must */ - if (need_rp_free) extend_info_free(rp); + /* Free rp */ + extend_info_free(rp); return status; } /** Given a parsed and decrypted INTRODUCE2, find the rendezvous point or - * return NULL and an error string if we can't. - */ - + * return NULL and an error string if we can't. Return a newly allocated + * extend_info_t* for the rendezvous point. */ static extend_info_t * find_rp_for_intro(const rend_intro_cell_t *intro, - uint8_t *need_free_out, char **err_msg_out) + char **err_msg_out) { extend_info_t *rp = NULL; char *err_msg = NULL; const char *rp_nickname = NULL; const node_t *node = NULL; - uint8_t need_free = 0; - if (!intro || !need_free_out) { + if (!intro) { if (err_msg_out) err_msg = tor_strdup("Bad parameters to find_rp_for_intro()"); @@ -1632,19 +1800,17 @@ find_rp_for_intro(const rend_intro_cell_t *intro, if (!rp) { if (err_msg_out) { tor_asprintf(&err_msg, - "Could build extend_info_t for router %s named " + "Couldn't build extend_info_t for router %s named " "in INTRODUCE2 cell", escaped_safe_str_client(rp_nickname)); } goto err; - } else { - need_free = 1; } } else if (intro->version == 2) { - rp = intro->u.v2.extend_info; + rp = extend_info_dup(intro->u.v2.extend_info); } else if (intro->version == 3) { - rp = intro->u.v3.extend_info; + rp = extend_info_dup(intro->u.v3.extend_info); } else { if (err_msg_out) { tor_asprintf(&err_msg, @@ -1655,15 +1821,27 @@ find_rp_for_intro(const rend_intro_cell_t *intro, goto err; } + /* Make sure the RP we are being asked to connect to is _not_ a private + * address unless it's allowed. Let's avoid to build a circuit to our + * second middle node and fail right after when extending to the RP. */ + if (!extend_info_addr_is_allowed(&rp->addr)) { + if (err_msg_out) { + tor_asprintf(&err_msg, + "Relay IP in INTRODUCE2 cell is private address."); + } + extend_info_free(rp); + rp = NULL; + goto err; + } goto done; err: - if (err_msg_out) *err_msg_out = err_msg; - else tor_free(err_msg); + if (err_msg_out) + *err_msg_out = err_msg; + else + tor_free(err_msg); done: - if (rp && need_free_out) *need_free_out = need_free; - return rp; } @@ -2330,37 +2508,6 @@ rend_service_parse_intro_plaintext( return status; } -/** Do validity checks on a parsed intro cell before decryption; some of - * these are not done in rend_service_begin_parse_intro() itself because - * they depend on a lot of other state and would make it hard to unit test. - * Returns >= 0 if successful or < 0 if the intro cell is invalid, and - * optionally writes out an error message for logging. If an err_msg - * pointer is provided, it is the caller's responsibility to free any - * provided message. - */ - -int -rend_service_validate_intro_early(const rend_intro_cell_t *intro, - char **err_msg_out) -{ - int status = 0; - - if (!intro) { - if (err_msg_out) - *err_msg_out = - tor_strdup("NULL intro cell passed to " - "rend_service_validate_intro_early()"); - - status = -1; - goto err; - } - - /* TODO */ - - err: - return status; -} - /** Do validity checks on a parsed intro cell after decryption; some of * these are not done in rend_service_parse_intro_plaintext() itself because * they depend on a lot of other state and would make it hard to unit test. @@ -2495,38 +2642,41 @@ rend_service_launch_establish_intro(rend_service_t *service, safe_str_client(extend_info_describe(intro->extend_info))); return -1; } + /* We must have the same exit node even if cannibalized. */ + tor_assert(tor_memeq(intro->extend_info->identity_digest, + launched->build_state->chosen_exit->identity_digest, + DIGEST_LEN)); - if (tor_memneq(intro->extend_info->identity_digest, - launched->build_state->chosen_exit->identity_digest, DIGEST_LEN)) { - char cann[HEX_DIGEST_LEN+1], orig[HEX_DIGEST_LEN+1]; - base16_encode(cann, sizeof(cann), - launched->build_state->chosen_exit->identity_digest, - DIGEST_LEN); - base16_encode(orig, sizeof(orig), - intro->extend_info->identity_digest, DIGEST_LEN); - log_info(LD_REND, "The intro circuit we just cannibalized ends at $%s, " - "but we requested an intro circuit to $%s. Updating " - "our service.", cann, orig); - extend_info_free(intro->extend_info); - intro->extend_info = extend_info_dup(launched->build_state->chosen_exit); - } - - launched->rend_data = tor_malloc_zero(sizeof(rend_data_t)); - strlcpy(launched->rend_data->onion_address, service->service_id, - sizeof(launched->rend_data->onion_address)); - memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN); + launched->rend_data = rend_data_service_create(service->service_id, + service->pk_digest, NULL, + service->auth_type); launched->intro_key = crypto_pk_dup_key(intro->intro_key); if (launched->base_.state == CIRCUIT_STATE_OPEN) rend_service_intro_has_opened(launched); return 0; } -/** Return the number of introduction points that are or have been - * established for the given service address in <b>query</b>. */ -static int -count_established_intro_points(const char *query) +/** Return the number of introduction points that are established for the + * given service. */ +static unsigned int +count_established_intro_points(const rend_service_t *service) { - int num_ipos = 0; + unsigned int num = 0; + + SMARTLIST_FOREACH(service->intro_nodes, rend_intro_point_t *, intro, + num += intro->circuit_established + ); + return num; +} + +/** Return the number of introduction points that are or are being + * established for the given service. This function iterates over all + * circuit and count those that are linked to the service and are waiting + * for the intro point to respond. */ +static unsigned int +count_intro_point_circuits(const rend_service_t *service) +{ + unsigned int num_ipos = 0; SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { if (!circ->marked_for_close && circ->state == CIRCUIT_STATE_OPEN && @@ -2534,7 +2684,8 @@ count_established_intro_points(const char *query) circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) { origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); if (oc->rend_data && - !rend_cmp_service_ids(query, oc->rend_data->onion_address)) + !rend_cmp_service_ids(service->service_id, + oc->rend_data->onion_address)) num_ipos++; } } @@ -2571,16 +2722,27 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) circuit->rend_data->rend_pk_digest); if (!service) { log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %u.", - serviceid, (unsigned)circuit->base_.n_circ_id); + safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id); reason = END_CIRC_REASON_NOSUCHSERVICE; goto err; } /* If we already have enough introduction circuits for this service, - * redefine this one as a general circuit or close it, depending. */ - if (count_established_intro_points(serviceid) > - (int)service->n_intro_points_wanted) { /* XXX023 remove cast */ + * redefine this one as a general circuit or close it, depending. + * Substract the amount of expiring nodes here since the circuits are + * still opened. */ + if ((count_intro_point_circuits(service) - + smartlist_len(service->expiring_nodes)) > + service->n_intro_points_wanted) { const or_options_t *options = get_options(); + /* Remove the intro point associated with this circuit, it's being + * repurposed or closed thus cleanup memory. */ + rend_intro_point_t *intro = find_intro_point(circuit); + if (intro != NULL) { + smartlist_remove(service->intro_nodes, intro); + rend_intro_point_free(intro); + } + if (options->ExcludeNodes) { /* XXXX in some future version, we can test whether the transition is allowed or not given the actual nodes in the circuit. But for now, @@ -2679,6 +2841,7 @@ rend_service_intro_established(origin_circuit_t *circuit, size_t request_len) { rend_service_t *service; + rend_intro_point_t *intro; char serviceid[REND_SERVICE_ID_LEN_BASE32+1]; (void) request; (void) request_len; @@ -2696,6 +2859,19 @@ rend_service_intro_established(origin_circuit_t *circuit, (unsigned)circuit->base_.n_circ_id); goto err; } + /* We've just successfully established a intro circuit to one of our + * introduction point, account for it. */ + intro = find_intro_point(circuit); + if (intro == NULL) { + log_warn(LD_REND, + "Introduction circuit established without a rend_intro_point_t " + "object for service %s on circuit %u", + safe_str_client(serviceid), (unsigned)circuit->base_.n_circ_id); + goto err; + } + intro->circuit_established = 1; + /* We might not have every introduction point ready but at this point we + * know that the descriptor needs to be uploaded. */ service->desc_is_dirty = time(NULL); circuit_change_purpose(TO_CIRCUIT(circuit), CIRCUIT_PURPOSE_S_INTRO); @@ -2811,7 +2987,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) /* Append the cpath entry. */ hop->state = CPATH_STATE_OPEN; /* set the windows to default. these are the windows - * that bob thinks alice has. + * that the service thinks the client has. */ hop->package_window = circuit_initial_package_window(); hop->deliver_window = CIRCWINDOW_START; @@ -2870,6 +3046,24 @@ find_intro_circuit(rend_intro_point_t *intro, const char *pk_digest) return NULL; } +/** Return the corresponding introdution point using the circuit <b>circ</b> + * found in the <b>service</b>. NULL is returned if not found. */ +static rend_intro_point_t * +find_expiring_intro_point(rend_service_t *service, origin_circuit_t *circ) +{ + tor_assert(service); + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO || + TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_INTRO); + + SMARTLIST_FOREACH(service->expiring_nodes, rend_intro_point_t *, + intro_point, + if (crypto_pk_eq_keys(intro_point->intro_key, circ->intro_key)) { + return intro_point; + }); + + return NULL; +} + /** Return a pointer to the rend_intro_point_t corresponding to the * service-side introduction circuit <b>circ</b>. */ static rend_intro_point_t * @@ -2899,14 +3093,16 @@ find_intro_point(origin_circuit_t *circ) return NULL; } -/** Determine the responsible hidden service directories for the - * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them; - * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging - * purposes. */ -static void +/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b> + * associated with the rend_service_descriptor_t <b>renddesc</b> to + * the responsible hidden service directories OR the hidden service + * directories specified by <b>hs_dirs</b>; <b>service_id</b> and + * <b>seconds_valid</b> are only passed for logging purposes. + */ +void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, - smartlist_t *descs, const char *service_id, - int seconds_valid) + smartlist_t *descs, smartlist_t *hs_dirs, + const char *service_id, int seconds_valid) { int i, j, failed_upload = 0; smartlist_t *responsible_dirs = smartlist_new(); @@ -2914,19 +3110,27 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, routerstatus_t *hs_dir; for (i = 0; i < smartlist_len(descs); i++) { rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i); - /* Determine responsible dirs. */ - if (hid_serv_get_responsible_directories(responsible_dirs, - desc->desc_id) < 0) { - log_warn(LD_REND, "Could not determine the responsible hidden service " - "directories to post descriptors to."); - smartlist_free(responsible_dirs); - smartlist_free(successful_uploads); - return; + /** If any HSDirs are specified, they should be used instead of + * the responsible directories */ + if (hs_dirs && smartlist_len(hs_dirs) > 0) { + smartlist_add_all(responsible_dirs, hs_dirs); + } else { + /* Determine responsible dirs. */ + if (hid_serv_get_responsible_directories(responsible_dirs, + desc->desc_id) < 0) { + log_warn(LD_REND, "Could not determine the responsible hidden service " + "directories to post descriptors to."); + control_event_hs_descriptor_upload(service_id, + "UNKNOWN", + "UNKNOWN"); + goto done; + } } for (j = 0; j < smartlist_len(responsible_dirs); j++) { char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; char *hs_dir_ip; const node_t *node; + rend_data_t *rend_data; hs_dir = smartlist_get(responsible_dirs, j); if (smartlist_contains_digest(renddesc->successful_uploads, hs_dir->identity_digest)) @@ -2942,12 +3146,19 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, continue; } /* Send publish request. */ - directory_initiate_command_routerstatus(hs_dir, + + /* We need the service ID to identify which service did the upload + * request. Lookup is made in rend_service_desc_has_uploaded(). */ + rend_data = rend_data_client_create(service_id, desc->desc_id, NULL, + REND_NO_AUTH); + directory_initiate_command_routerstatus_rend(hs_dir, DIR_PURPOSE_UPLOAD_RENDDESC_V2, - ROUTER_PURPOSE_GENERAL, - DIRIND_ANONYMOUS, NULL, - desc->desc_str, - strlen(desc->desc_str), 0); + ROUTER_PURPOSE_GENERAL, + DIRIND_ANONYMOUS, NULL, + desc->desc_str, + strlen(desc->desc_str), + 0, rend_data); + rend_data_free(rend_data); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc->desc_id, DIGEST_LEN); hs_dir_ip = tor_dup_ip(hs_dir->addr); @@ -2961,6 +3172,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, hs_dir->nickname, hs_dir_ip, hs_dir->or_port); + control_event_hs_descriptor_upload(service_id, + hs_dir->identity_digest, + desc_id_base32); tor_free(hs_dir_ip); /* Remember successful upload to this router for next time. */ if (!smartlist_contains_digest(successful_uploads, @@ -2988,6 +3202,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, } }); } + done: smartlist_free(responsible_dirs); smartlist_free(successful_uploads); } @@ -3005,39 +3220,72 @@ upload_service_descriptor(rend_service_t *service) rendpostperiod = get_options()->RendPostPeriod; - /* Upload descriptor? */ - if (get_options()->PublishHidServDescriptors) { - networkstatus_t *c = networkstatus_get_latest_consensus(); - if (c && smartlist_len(c->routerstatus_list) > 0) { - int seconds_valid, i, j, num_descs; - smartlist_t *descs = smartlist_new(); - smartlist_t *client_cookies = smartlist_new(); - /* Either upload a single descriptor (including replicas) or one - * descriptor for each authorized client in case of authorization - * type 'stealth'. */ - num_descs = service->auth_type == REND_STEALTH_AUTH ? - smartlist_len(service->clients) : 1; - for (j = 0; j < num_descs; j++) { - crypto_pk_t *client_key = NULL; - rend_authorized_client_t *client = NULL; - smartlist_clear(client_cookies); - switch (service->auth_type) { - case REND_NO_AUTH: - /* Do nothing here. */ - break; - case REND_BASIC_AUTH: - SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, - cl, smartlist_add(client_cookies, cl->descriptor_cookie)); - break; - case REND_STEALTH_AUTH: - client = smartlist_get(service->clients, j); - client_key = client->client_key; - smartlist_add(client_cookies, client->descriptor_cookie); - break; - } - /* Encode the current descriptor. */ + networkstatus_t *c = networkstatus_get_latest_consensus(); + if (c && smartlist_len(c->routerstatus_list) > 0) { + int seconds_valid, i, j, num_descs; + smartlist_t *descs = smartlist_new(); + smartlist_t *client_cookies = smartlist_new(); + /* Either upload a single descriptor (including replicas) or one + * descriptor for each authorized client in case of authorization + * type 'stealth'. */ + num_descs = service->auth_type == REND_STEALTH_AUTH ? + smartlist_len(service->clients) : 1; + for (j = 0; j < num_descs; j++) { + crypto_pk_t *client_key = NULL; + rend_authorized_client_t *client = NULL; + smartlist_clear(client_cookies); + switch (service->auth_type) { + case REND_NO_AUTH: + /* Do nothing here. */ + break; + case REND_BASIC_AUTH: + SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, + cl, smartlist_add(client_cookies, cl->descriptor_cookie)); + break; + case REND_STEALTH_AUTH: + client = smartlist_get(service->clients, j); + client_key = client->client_key; + smartlist_add(client_cookies, client->descriptor_cookie); + break; + } + /* Encode the current descriptor. */ + seconds_valid = rend_encode_v2_descriptors(descs, service->desc, + now, 0, + service->auth_type, + client_key, + client_cookies); + if (seconds_valid < 0) { + log_warn(LD_BUG, "Internal error: couldn't encode service " + "descriptor; not uploading."); + smartlist_free(descs); + smartlist_free(client_cookies); + return; + } + rend_get_service_id(service->desc->pk, serviceid); + if (get_options()->PublishHidServDescriptors) { + /* Post the current descriptors to the hidden service directories. */ + log_info(LD_REND, "Launching upload for hidden service %s", + serviceid); + directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, + seconds_valid); + } + /* Free memory for descriptors. */ + for (i = 0; i < smartlist_len(descs); i++) + rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + smartlist_clear(descs); + /* Update next upload time. */ + if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + > rendpostperiod) + service->next_upload_time = now + rendpostperiod; + else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) + service->next_upload_time = now + seconds_valid + 1; + else + service->next_upload_time = now + seconds_valid - + REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1; + /* Post also the next descriptors, if necessary. */ + if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) { seconds_valid = rend_encode_v2_descriptors(descs, service->desc, - now, 0, + now, 1, service->auth_type, client_key, client_cookies); @@ -3048,51 +3296,23 @@ upload_service_descriptor(rend_service_t *service) smartlist_free(client_cookies); return; } - /* Post the current descriptors to the hidden service directories. */ - rend_get_service_id(service->desc->pk, serviceid); - log_info(LD_REND, "Launching upload for hidden service %s", - serviceid); - directory_post_to_hs_dir(service->desc, descs, serviceid, - seconds_valid); + if (get_options()->PublishHidServDescriptors) { + directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, + seconds_valid); + } /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); smartlist_clear(descs); - /* Update next upload time. */ - if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS - > rendpostperiod) - service->next_upload_time = now + rendpostperiod; - else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) - service->next_upload_time = now + seconds_valid + 1; - else - service->next_upload_time = now + seconds_valid - - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1; - /* Post also the next descriptors, if necessary. */ - if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) { - seconds_valid = rend_encode_v2_descriptors(descs, service->desc, - now, 1, - service->auth_type, - client_key, - client_cookies); - if (seconds_valid < 0) { - log_warn(LD_BUG, "Internal error: couldn't encode service " - "descriptor; not uploading."); - smartlist_free(descs); - smartlist_free(client_cookies); - return; - } - directory_post_to_hs_dir(service->desc, descs, serviceid, - seconds_valid); - /* Free memory for descriptors. */ - for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); - smartlist_clear(descs); - } } - smartlist_free(descs); - smartlist_free(client_cookies); - uploaded = 1; + } + smartlist_free(descs); + smartlist_free(client_cookies); + uploaded = 1; + if (get_options()->PublishHidServDescriptors) { log_info(LD_REND, "Successfully uploaded v2 rend descriptors!"); + } else { + log_info(LD_REND, "Successfully stored created v2 rend descriptors!"); } } @@ -3126,14 +3346,8 @@ intro_point_should_expire_now(rend_intro_point_t *intro, return 0; } - if (intro->time_expiring != -1) { - /* We've already started expiring this intro point. *Don't* let - * this function's result 'flap'. */ - return 1; - } - if (intro_point_accepted_intro_count(intro) >= - INTRO_POINT_LIFETIME_INTRODUCTIONS) { + intro->max_introductions) { /* This intro point has been used too many times. Expire it now. */ return 1; } @@ -3142,9 +3356,8 @@ intro_point_should_expire_now(rend_intro_point_t *intro, /* This intro point has been published, but we haven't picked an * expiration time for it. Pick one now. */ int intro_point_lifetime_seconds = - INTRO_POINT_LIFETIME_MIN_SECONDS + - crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS - - INTRO_POINT_LIFETIME_MIN_SECONDS); + crypto_rand_int_range(INTRO_POINT_LIFETIME_MIN_SECONDS, + INTRO_POINT_LIFETIME_MAX_SECONDS); /* Start the expiration timer now, rather than when the intro * point was first published. There shouldn't be much of a time @@ -3158,48 +3371,160 @@ intro_point_should_expire_now(rend_intro_point_t *intro, return (now >= intro->time_to_expire); } +/** Iterate over intro points in the given service and remove the invalid + * ones. For an intro point object to be considered invalid, the circuit + * _and_ node need to have disappeared. + * + * If the intro point should expire, it's placed into the expiring_nodes + * list of the service and removed from the active intro nodes list. + * + * If <b>exclude_nodes</b> is not NULL, add the valid nodes to it. + * + * If <b>retry_nodes</b> is not NULL, add the valid node to it if the + * circuit disappeared but the node is still in the consensus. */ +static void +remove_invalid_intro_points(rend_service_t *service, + smartlist_t *exclude_nodes, + smartlist_t *retry_nodes, time_t now) +{ + tor_assert(service); + + SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *, + intro) { + /* Find the introduction point node object. */ + const node_t *node = + node_get_by_id(intro->extend_info->identity_digest); + /* Find the intro circuit, this might be NULL. */ + origin_circuit_t *intro_circ = + find_intro_circuit(intro, service->pk_digest); + + /* Add the valid node to the exclusion list so we don't try to establish + * an introduction point to it again. */ + if (node && exclude_nodes) { + smartlist_add(exclude_nodes, (void*) node); + } + + /* First, make sure we still have a valid circuit for this intro point. + * If we dont, we'll give up on it and make a new one. */ + if (intro_circ == NULL) { + log_info(LD_REND, "Attempting to retry on %s as intro point for %s" + " (circuit disappeared).", + safe_str_client(extend_info_describe(intro->extend_info)), + safe_str_client(service->service_id)); + /* We've lost the circuit for this intro point, flag it so it can be + * accounted for when considiring uploading a descriptor. */ + intro->circuit_established = 0; + + /* Node is gone or we've reached our maximum circuit creationg retry + * count, clean up everything, we'll find a new one. */ + if (node == NULL || + intro->circuit_retries >= MAX_INTRO_POINT_CIRCUIT_RETRIES) { + rend_intro_point_free(intro); + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + /* We've just killed the intro point, nothing left to do. */ + continue; + } + + /* The intro point is still alive so let's try to use it again because + * we have a published descriptor containing it. Keep the intro point + * in the intro_nodes list because it's still valid, we are rebuilding + * a circuit to it. */ + if (retry_nodes) { + smartlist_add(retry_nodes, intro); + } + } + /* else, the circuit is valid so in both cases, node being alive or not, + * we leave the circuit and intro point object as is. Closing the + * circuit here would leak new consensus timing and freeing the intro + * point object would make the intro circuit unusable. */ + + /* Now, check if intro point should expire. If it does, queue it so + * it can be cleaned up once it has been replaced properly. */ + if (intro_point_should_expire_now(intro, now)) { + log_info(LD_REND, "Expiring %s as intro point for %s.", + safe_str_client(extend_info_describe(intro->extend_info)), + safe_str_client(service->service_id)); + smartlist_add(service->expiring_nodes, intro); + SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); + /* Intro point is expired, we need a new one thus don't consider it + * anymore has a valid established intro point. */ + intro->circuit_established = 0; + } + } SMARTLIST_FOREACH_END(intro); +} + +/** A new descriptor has been successfully uploaded for the given + * <b>rend_data</b>. Remove and free the expiring nodes from the associated + * service. */ +void +rend_service_desc_has_uploaded(const rend_data_t *rend_data) +{ + rend_service_t *service; + + tor_assert(rend_data); + + service = rend_service_get_by_service_id(rend_data->onion_address); + if (service == NULL) { + return; + } + + SMARTLIST_FOREACH_BEGIN(service->expiring_nodes, rend_intro_point_t *, + intro) { + origin_circuit_t *intro_circ = + find_intro_circuit(intro, service->pk_digest); + if (intro_circ != NULL) { + circuit_mark_for_close(TO_CIRCUIT(intro_circ), + END_CIRC_REASON_FINISHED); + } + SMARTLIST_DEL_CURRENT(service->expiring_nodes, intro); + rend_intro_point_free(intro); + } SMARTLIST_FOREACH_END(intro); +} + /** For every service, check how many intro points it currently has, and: + * - Invalidate introdution points based on specific criteria, see + * remove_invalid_intro_points comments. * - Pick new intro points as necessary. * - Launch circuits to any new intro points. + * + * This is called once a second by the main loop. */ void -rend_services_introduce(void) +rend_consider_services_intro_points(void) { - int i,j,r; - const node_t *node; - rend_service_t *service; - rend_intro_point_t *intro; - int intro_point_set_changed, prev_intro_nodes; - unsigned int n_intro_points_unexpired; - unsigned int n_intro_points_to_open; + int i; time_t now; const or_options_t *options = get_options(); - /* List of nodes we need to _exclude_ when choosing a new node to establish - * an intro point to. */ + /* List of nodes we need to _exclude_ when choosing a new node to + * establish an intro point to. */ smartlist_t *exclude_nodes; + /* List of nodes we need to retry to build a circuit on them because the + * node is valid but circuit died. */ + smartlist_t *retry_nodes; if (!have_completed_a_circuit()) return; exclude_nodes = smartlist_new(); + retry_nodes = smartlist_new(); now = time(NULL); - for (i=0; i < smartlist_len(rend_service_list); ++i) { + SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, service) { + int r; + /* Number of intro points we want to open and add to the intro nodes + * list of the service. */ + unsigned int n_intro_points_to_open; + /* Have an unsigned len so we can use it to compare values else gcc is + * not happy with unmatching signed comparaison. */ + unsigned int intro_nodes_len; + /* Different service are allowed to have the same introduction point as + * long as they are on different circuit thus why we clear this list. */ smartlist_clear(exclude_nodes); - service = smartlist_get(rend_service_list, i); - - tor_assert(service); - - /* intro_point_set_changed becomes non-zero iff the set of intro - * points to be published in service's descriptor has changed. */ - intro_point_set_changed = 0; + smartlist_clear(retry_nodes); - /* n_intro_points_unexpired collects the number of non-expiring - * intro points we have, so that we know how many new intro - * circuits we need to launch for this service. */ - n_intro_points_unexpired = 0; - - if (now > service->intro_period_started+INTRO_CIRC_RETRY_PERIOD) { + /* This retry period is important here so we don't stress circuit + * creation. */ + if (now > service->intro_period_started + INTRO_CIRC_RETRY_PERIOD) { /* One period has elapsed; we can try building circuits again. */ service->intro_period_started = now; service->n_intro_circuits_launched = 0; @@ -3210,116 +3535,58 @@ rend_services_introduce(void) continue; } - /* Find out which introduction points we have in progress for this - service. */ - SMARTLIST_FOREACH_BEGIN(service->intro_nodes, rend_intro_point_t *, - intro) { - origin_circuit_t *intro_circ = - find_intro_circuit(intro, service->pk_digest); - - if (intro->time_expiring + INTRO_POINT_EXPIRATION_GRACE_PERIOD > now) { - /* This intro point has completely expired. Remove it, and - * mark the circuit for close if it's still alive. */ - if (intro_circ != NULL && - intro_circ->base_.purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) { - circuit_mark_for_close(TO_CIRCUIT(intro_circ), - END_CIRC_REASON_FINISHED); - } - rend_intro_point_free(intro); - intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ - SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); - /* We don't need to set intro_point_set_changed here, because - * this intro point wouldn't have been published in a current - * descriptor anyway. */ - continue; - } + /* Cleanup the invalid intro points and save the node objects, if apply, + * in the exclude_nodes and retry_nodes list. */ + remove_invalid_intro_points(service, exclude_nodes, retry_nodes, now); - node = node_get_by_id(intro->extend_info->identity_digest); - if (!node || !intro_circ) { - int removing_this_intro_point_changes_the_intro_point_set = 1; - log_info(LD_REND, "Giving up on %s as intro point for %s" - " (circuit disappeared).", + /* Let's try to rebuild circuit on the nodes we want to retry on. */ + SMARTLIST_FOREACH_BEGIN(retry_nodes, rend_intro_point_t *, intro) { + r = rend_service_launch_establish_intro(service, intro); + if (r < 0) { + log_warn(LD_REND, "Error launching circuit to node %s for service %s.", safe_str_client(extend_info_describe(intro->extend_info)), safe_str_client(service->service_id)); - rend_service_note_removing_intro_point(service, intro); - if (intro->time_expiring != -1) { - log_info(LD_REND, "We were already expiring the intro point; " - "no need to mark the HS descriptor as dirty over this."); - removing_this_intro_point_changes_the_intro_point_set = 0; - } else if (intro->listed_in_last_desc) { - log_info(LD_REND, "The intro point we are giving up on was " - "included in the last published descriptor. " - "Marking current descriptor as dirty."); - service->desc_is_dirty = now; - } + /* Unable to launch a circuit to that intro point, remove it from + * the valid list so we can create a new one. */ + smartlist_remove(service->intro_nodes, intro); rend_intro_point_free(intro); - intro = NULL; /* SMARTLIST_DEL_CURRENT takes a name, not a value. */ - SMARTLIST_DEL_CURRENT(service->intro_nodes, intro); - if (removing_this_intro_point_changes_the_intro_point_set) - intro_point_set_changed = 1; - } - - if (intro != NULL && intro_point_should_expire_now(intro, now)) { - log_info(LD_REND, "Expiring %s as intro point for %s.", - safe_str_client(extend_info_describe(intro->extend_info)), - safe_str_client(service->service_id)); - - rend_service_note_removing_intro_point(service, intro); - - /* The polite (and generally Right) way to expire an intro - * point is to establish a new one to replace it, publish a - * new descriptor that doesn't list any expiring intro points, - * and *then*, once our upload attempts for the new descriptor - * have ended (whether in success or failure), close the - * expiring intro points. - * - * Unfortunately, we can't find out when the new descriptor - * has actually been uploaded, so we'll have to settle for a - * five-minute timer. Start it. XXXX024 This sucks. */ - intro->time_expiring = now; - - intro_point_set_changed = 1; + continue; } - - if (intro != NULL && intro->time_expiring == -1) - ++n_intro_points_unexpired; - - /* Add the valid node to the exclusion list so we don't try to establish - * an introduction point to it again. */ - if (node) - smartlist_add(exclude_nodes, (void*)node); + intro->circuit_retries++; } SMARTLIST_FOREACH_END(intro); - if (!intro_point_set_changed && - (n_intro_points_unexpired >= service->n_intro_points_wanted)) { + /* Avoid mismatched signed comparaison below. */ + intro_nodes_len = (unsigned int) smartlist_len(service->intro_nodes); + + /* Quiescent state, no node expiring and we have more or the amount of + * wanted node for this service. Proceed to the next service. Could be + * more because we launch two preemptive circuits if our intro nodes + * list is empty. */ + if (smartlist_len(service->expiring_nodes) == 0 && + intro_nodes_len >= service->n_intro_points_wanted) { continue; } - /* Remember how many introduction circuits we started with. - * - * prev_intro_nodes serves a different purpose than - * n_intro_points_unexpired -- this variable tells us where our - * previously-created intro points end and our new ones begin in - * the intro-point list, so we don't have to launch the circuits - * at the same time as we create the intro points they correspond - * to. XXXX This is daft. */ - prev_intro_nodes = smartlist_len(service->intro_nodes); - - /* We have enough directory information to start establishing our - * intro points. We want to end up with n_intro_points_wanted - * intro points, but if we're just starting, we launch two extra - * circuits and use the first n_intro_points_wanted that complete. - * - * The ones after the first three will be converted to 'general' - * internal circuits in rend_service_intro_has_opened(), and then - * we'll drop them from the list of intro points next time we - * go through the above "find out which introduction points we have - * in progress" loop. */ - n_intro_points_to_open = (service->n_intro_points_wanted + - (prev_intro_nodes == 0 ? 2 : 0)); - for (j = (int)n_intro_points_unexpired; - j < (int)n_intro_points_to_open; - ++j) { /* XXXX remove casts */ + /* Number of intro points we want to open which is the wanted amount + * minus the current amount of valid nodes. */ + n_intro_points_to_open = service->n_intro_points_wanted - intro_nodes_len; + if (intro_nodes_len == 0) { + /* We want to end up with n_intro_points_wanted intro points, but if + * we have no intro points at all (chances are they all cycled or we + * are starting up), we launch NUM_INTRO_POINTS_EXTRA extra circuits + * and use the first n_intro_points_wanted that complete. See proposal + * #155, section 4 for the rationale of this which is purely for + * performance. + * + * The ones after the first n_intro_points_to_open will be converted + * to 'general' internal circuits in rend_service_intro_has_opened(), + * and then we'll drop them from the list of intro points. */ + n_intro_points_to_open += NUM_INTRO_POINTS_EXTRA; + } + + for (i = 0; i < (int) n_intro_points_to_open; i++) { + const node_t *node; + rend_intro_point_t *intro; router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC; if (get_options()->AllowInvalid_ & ALLOW_INVALID_INTRODUCTION) flags |= CRN_ALLOW_INVALID; @@ -3327,45 +3594,44 @@ rend_services_introduce(void) options->ExcludeNodes, flags); if (!node) { log_warn(LD_REND, - "Could only establish %d introduction points for %s; " + "We only have %d introduction points established for %s; " "wanted %u.", - smartlist_len(service->intro_nodes), service->service_id, + smartlist_len(service->intro_nodes), + safe_str_client(service->service_id), n_intro_points_to_open); break; } - intro_point_set_changed = 1; - /* Add the choosen node to the exclusion list in order to avoid to pick - * it again in the next iteration. */ + /* Add the choosen node to the exclusion list in order to avoid to + * pick it again in the next iteration. */ smartlist_add(exclude_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_pk_new(); - tor_assert(!crypto_pk_generate_key(intro->intro_key)); + const int fail = crypto_pk_generate_key(intro->intro_key); + tor_assert(!fail); intro->time_published = -1; intro->time_to_expire = -1; - intro->time_expiring = -1; + intro->max_introductions = + crypto_rand_int_range(INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS, + INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS); smartlist_add(service->intro_nodes, intro); log_info(LD_REND, "Picked router %s as an intro point for %s.", safe_str_client(node_describe(node)), safe_str_client(service->service_id)); - } - - /* If there's no need to launch new circuits, stop here. */ - if (!intro_point_set_changed) - continue; - - /* Establish new introduction points. */ - for (j=prev_intro_nodes; j < smartlist_len(service->intro_nodes); ++j) { - intro = smartlist_get(service->intro_nodes, j); + /* Establish new introduction circuit to our chosen intro point. */ r = rend_service_launch_establish_intro(service, intro); - if (r<0) { + if (r < 0) { log_warn(LD_REND, "Error launching circuit to node %s for service %s.", safe_str_client(extend_info_describe(intro->extend_info)), safe_str_client(service->service_id)); + /* This funcion will be called again by the main loop so this intro + * point without a intro circuit will be retried on or removed after + * a maximum number of attempts. */ } } - } + } SMARTLIST_FOREACH_END(service); smartlist_free(exclude_nodes); + smartlist_free(retry_nodes); } #define MIN_REND_INITIAL_POST_DELAY (30) @@ -3389,9 +3655,6 @@ rend_consider_services_upload(time_t now) MIN_REND_INITIAL_POST_DELAY_TESTING : MIN_REND_INITIAL_POST_DELAY); - if (!get_options()->PublishHidServDescriptors) - return; - for (i=0; i < smartlist_len(rend_service_list); ++i) { service = smartlist_get(rend_service_list, i); if (!service->next_upload_time) { /* never been uploaded yet */ @@ -3400,9 +3663,14 @@ rend_consider_services_upload(time_t now) service->next_upload_time = now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod); } - if (service->next_upload_time < now || + /* Does every introduction points have been established? */ + unsigned int intro_points_ready = + count_established_intro_points(service) >= + service->n_intro_points_wanted; + if (intro_points_ready && + (service->next_upload_time < now || (service->desc_is_dirty && - service->desc_is_dirty < now-rendinitialpostdelay)) { + service->desc_is_dirty < now-rendinitialpostdelay))) { /* if it's time, or if the directory servers have a wrong service * descriptor and ours has been stable for rendinitialpostdelay seconds, * upload a new one of each format. */ @@ -3567,6 +3835,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn, serviceid, (unsigned)circ->base_.n_circ_id); return -2; } + if (service->max_streams_per_circuit > 0) { + /* Enforce the streams-per-circuit limit, and refuse to provide a + * mapping if this circuit will exceed the limit. */ +#define MAX_STREAM_WARN_INTERVAL 600 + static struct ratelim_t stream_ratelim = + RATELIM_INIT(MAX_STREAM_WARN_INTERVAL); + if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) { + log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND, + "Maximum streams per circuit limit reached on rendezvous " + "circuit %u; %s. Circuit has %d out of %d streams.", + (unsigned)circ->base_.n_circ_id, + service->max_streams_close_circuit ? + "closing circuit" : + "ignoring open stream request", + circ->rend_data->nr_streams, + service->max_streams_per_circuit); + return service->max_streams_close_circuit ? -2 : -1; + } + } matching_ports = smartlist_new(); SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, { diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 754f7c358c..101b37e18d 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,6 +15,7 @@ #include "or.h" typedef struct rend_intro_cell_s rend_intro_cell_t; +typedef struct rend_service_port_config_s rend_service_port_config_t; #ifdef RENDSERVICE_PRIVATE @@ -69,7 +70,7 @@ int rend_config_services(const or_options_t *options, int validate_only); int rend_service_load_all_keys(void); void rend_services_add_filenames_to_lists(smartlist_t *open_lst, smartlist_t *stat_lst); -void rend_services_introduce(void); +void rend_consider_services_intro_points(void); void rend_consider_services_upload(time_t now); void rend_hsdir_routers_changed(void); void rend_consider_descriptor_republication(void); @@ -79,8 +80,9 @@ int rend_service_intro_established(origin_circuit_t *circuit, const uint8_t *request, size_t request_len); void rend_service_rendezvous_has_opened(origin_circuit_t *circuit); -int rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request, - size_t request_len); +int rend_service_receive_introduction(origin_circuit_t *circuit, + const uint8_t *request, + size_t request_len); int rend_service_decrypt_intro(rend_intro_cell_t *request, crypto_pk_t *key, char **err_msg_out); @@ -91,8 +93,6 @@ rend_intro_cell_t * rend_service_begin_parse_intro(const uint8_t *request, char **err_msg_out); int rend_service_parse_intro_plaintext(rend_intro_cell_t *intro, char **err_msg_out); -int rend_service_validate_intro_early(const rend_intro_cell_t *intro, - char **err_msg_out); int rend_service_validate_intro_late(const rend_intro_cell_t *intro, char **err_msg_out); void rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc); @@ -101,5 +101,30 @@ int rend_service_set_connection_addr_port(edge_connection_t *conn, void rend_service_dump_stats(int severity); void rend_service_free_all(void); +rend_service_port_config_t *rend_service_parse_port_config(const char *string, + const char *sep, + char **err_msg_out); +void rend_service_port_config_free(rend_service_port_config_t *p); + +/** Return value from rend_service_add_ephemeral. */ +typedef enum { + RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */ + RSAE_ADDREXISTS = -3, /**< Onion address collision */ + RSAE_BADPRIVKEY = -2, /**< Invalid public key */ + RSAE_INTERNAL = -1, /**< Internal error */ + RSAE_OKAY = 0 /**< Service added as expected */ +} rend_service_add_ephemeral_status_t; +rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk, + smartlist_t *ports, + int max_streams_per_circuit, + int max_streams_close_circuit, + char **service_id_out); +int rend_service_del_ephemeral(const char *service_id); + +void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, + smartlist_t *descs, smartlist_t *hs_dirs, + const char *service_id, int seconds_valid); +void rend_service_desc_has_uploaded(const rend_data_t *rend_data); + #endif diff --git a/src/or/rephist.c b/src/or/rephist.c index fe0997c891..04ed7aef0f 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -1,5 +1,5 @@ /* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -148,7 +148,7 @@ get_link_history(const char *from_id, const char *to_id) return NULL; if (tor_digest_is_zero(to_id)) return NULL; - lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id); + lhist = digestmap_get(orhist->link_history_map, to_id); if (!lhist) { lhist = tor_malloc_zero(sizeof(link_history_t)); rephist_total_alloc += sizeof(link_history_t); @@ -920,7 +920,7 @@ parse_possibly_bad_iso_time(const char *s, time_t *time_out) * that's about as much before <b>now</b> as <b>t</b> was before * <b>stored_at</b>. */ -static INLINE time_t +static inline time_t correct_time(time_t t, time_t now, time_t stored_at, time_t started_measuring) { if (t < started_measuring - 24*60*60*365) @@ -1190,7 +1190,7 @@ commit_max(bw_array_t *b) } /** Shift the current observation time of <b>b</b> forward by one second. */ -static INLINE void +static inline void advance_obs(bw_array_t *b) { int nextidx; @@ -1216,7 +1216,7 @@ advance_obs(bw_array_t *b) /** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second * <b>when</b>. */ -static INLINE void +static inline void add_obs(bw_array_t *b, time_t when, uint64_t n) { if (when < b->cur_obs_time) @@ -1250,6 +1250,18 @@ bw_array_new(void) return b; } +/** Free storage held by bandwidth array <b>b</b>. */ +static void +bw_array_free(bw_array_t *b) +{ + if (!b) { + return; + } + + rephist_total_alloc -= sizeof(bw_array_t); + tor_free(b); +} + /** Recent history of bandwidth observations for read operations. */ static bw_array_t *read_array = NULL; /** Recent history of bandwidth observations for write operations. */ @@ -1266,10 +1278,11 @@ static bw_array_t *dir_write_array = NULL; static void bw_arrays_init(void) { - tor_free(read_array); - tor_free(write_array); - tor_free(dir_read_array); - tor_free(dir_write_array); + bw_array_free(read_array); + bw_array_free(write_array); + bw_array_free(dir_read_array); + bw_array_free(dir_write_array); + read_array = bw_array_new(); write_array = bw_array_new(); dir_read_array = bw_array_new(); @@ -1780,6 +1793,7 @@ rep_hist_remove_predicted_ports(const smartlist_t *rmv_ports) SMARTLIST_FOREACH_BEGIN(predicted_ports_list, predicted_port_t *, pp) { if (bitarray_is_set(remove_ports, pp->port)) { tor_free(pp); + rephist_total_alloc -= sizeof(*pp); SMARTLIST_DEL_CURRENT(predicted_ports_list, pp); } } SMARTLIST_FOREACH_END(pp); @@ -1853,14 +1867,17 @@ any_predicted_circuits(time_t now) int rep_hist_circbuilding_dormant(time_t now) { + const or_options_t *options = get_options(); + if (any_predicted_circuits(now)) return 0; /* see if we'll still need to build testing circuits */ - if (server_mode(get_options()) && - (!check_whether_orport_reachable() || !circuit_enough_testing_circs())) + if (server_mode(options) && + (!check_whether_orport_reachable(options) || + !circuit_enough_testing_circs())) return 0; - if (!check_whether_dirport_reachable()) + if (!check_whether_dirport_reachable(options)) return 0; return 1; @@ -3026,21 +3043,21 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey) /* The number of cells that are supposed to be hidden from the adversary * by adding noise from the Laplace distribution. This value, divided by - * EPSILON, is Laplace parameter b. */ + * EPSILON, is Laplace parameter b. It must be greather than 0. */ #define REND_CELLS_DELTA_F 2048 /* Security parameter for obfuscating number of cells with a value between - * 0 and 1. Smaller values obfuscate observations more, but at the same + * ]0.0, 1.0]. Smaller values obfuscate observations more, but at the same * time make statistics less usable. */ #define REND_CELLS_EPSILON 0.3 /* The number of cells that are supposed to be hidden from the adversary * by rounding up to the next multiple of this number. */ #define REND_CELLS_BIN_SIZE 1024 -/* The number of service identities that are supposed to be hidden from - * the adversary by adding noise from the Laplace distribution. This - * value, divided by EPSILON, is Laplace parameter b. */ +/* The number of service identities that are supposed to be hidden from the + * adversary by adding noise from the Laplace distribution. This value, + * divided by EPSILON, is Laplace parameter b. It must be greater than 0. */ #define ONIONS_SEEN_DELTA_F 8 /* Security parameter for obfuscating number of service identities with a - * value between 0 and 1. Smaller values obfuscate observations more, but + * value between ]0.0, 1.0]. Smaller values obfuscate observations more, but * at the same time make statistics less usable. */ #define ONIONS_SEEN_EPSILON 0.3 /* The number of service identities that are supposed to be hidden from @@ -3172,10 +3189,19 @@ rep_hist_free_all(void) { hs_stats_free(hs_stats); digestmap_free(history_map, free_or_history); - tor_free(read_array); - tor_free(write_array); - tor_free(dir_read_array); - tor_free(dir_write_array); + + bw_array_free(read_array); + read_array = NULL; + + bw_array_free(write_array); + write_array = NULL; + + bw_array_free(dir_read_array); + dir_read_array = NULL; + + bw_array_free(dir_write_array); + dir_write_array = NULL; + tor_free(exit_bytes_read); tor_free(exit_bytes_written); tor_free(exit_streams); @@ -3190,5 +3216,8 @@ rep_hist_free_all(void) } rep_hist_desc_stats_term(); total_descriptor_downloads = 0; + + tor_assert(rephist_total_alloc == 0); + tor_assert(rephist_total_num == 0); } diff --git a/src/or/rephist.h b/src/or/rephist.h index f94b4e8ff1..145da97d02 100644 --- a/src/or/rephist.h +++ b/src/or/rephist.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 569e0736cb..23a1737b18 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -1,4 +1,4 @@ - /* Copyright (c) 2012-2015, The Tor Project, Inc. */ + /* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /* @@ -23,7 +23,7 @@ replaycache_free(replaycache_t *r) return; } - if (r->digests_seen) digestmap_free(r->digests_seen, tor_free_); + if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_); tor_free(r); } @@ -54,7 +54,7 @@ replaycache_new(time_t horizon, time_t interval) r->scrub_interval = interval; r->scrubbed = 0; r->horizon = horizon; - r->digests_seen = digestmap_new(); + r->digests_seen = digest256map_new(); err: return r; @@ -69,7 +69,7 @@ replaycache_add_and_test_internal( time_t *elapsed) { int rv = 0; - char digest[DIGEST_LEN]; + uint8_t digest[DIGEST256_LEN]; time_t *access_time; /* sanity check */ @@ -80,10 +80,10 @@ replaycache_add_and_test_internal( } /* compute digest */ - crypto_digest(digest, (const char *)data, len); + crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256); /* check map */ - access_time = digestmap_get(r->digests_seen, digest); + access_time = digest256map_get(r->digests_seen, digest); /* seen before? */ if (access_time != NULL) { @@ -114,7 +114,7 @@ replaycache_add_and_test_internal( /* No, so no hit and update the digest map with the current time */ access_time = tor_malloc(sizeof(*access_time)); *access_time = present; - digestmap_set(r->digests_seen, digest, access_time); + digest256map_set(r->digests_seen, digest, access_time); } /* now scrub the cache if it's time */ @@ -130,8 +130,8 @@ replaycache_add_and_test_internal( STATIC void replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) { - digestmap_iter_t *itr = NULL; - const char *digest; + digest256map_iter_t *itr = NULL; + const uint8_t *digest; void *valp; time_t *access_time; @@ -149,19 +149,19 @@ replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r) if (r->horizon == 0) return; /* okay, scrub time */ - itr = digestmap_iter_init(r->digests_seen); - while (!digestmap_iter_done(itr)) { - digestmap_iter_get(itr, &digest, &valp); + itr = digest256map_iter_init(r->digests_seen); + while (!digest256map_iter_done(itr)) { + digest256map_iter_get(itr, &digest, &valp); access_time = (time_t *)valp; /* aged out yet? */ if (*access_time < present - r->horizon) { /* Advance the iterator and remove this one */ - itr = digestmap_iter_next_rmv(r->digests_seen, itr); + itr = digest256map_iter_next_rmv(r->digests_seen, itr); /* Free the value removed */ tor_free(access_time); } else { /* Just advance the iterator */ - itr = digestmap_iter_next(r->digests_seen, itr); + itr = digest256map_iter_next(r->digests_seen, itr); } } diff --git a/src/or/replaycache.h b/src/or/replaycache.h index 9b9daf3831..64a6caf5f5 100644 --- a/src/or/replaycache.h +++ b/src/or/replaycache.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Tor Project, Inc. */ +/* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -26,7 +26,7 @@ struct replaycache_s { /* * Digest map: keys are digests, values are times the digest was last seen */ - digestmap_t *digests_seen; + digest256map_t *digests_seen; }; #endif /* REPLAYCACHE_PRIVATE */ diff --git a/src/or/router.c b/src/or/router.c index 2ddaa895fc..01316c1bc2 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #define ROUTER_PRIVATE @@ -26,9 +26,11 @@ #include "relay.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "statefile.h" +#include "torcert.h" #include "transports.h" #include "routerset.h" @@ -204,6 +206,8 @@ set_server_identity_key(crypto_pk_t *k) static void assert_identity_keys_ok(void) { + if (1) + return; tor_assert(client_identitykey); if (public_server_mode(get_options())) { /* assert that we have set the client and server keys to be equal */ @@ -265,8 +269,8 @@ client_identity_key_is_set(void) /** Return the key certificate for this v3 (voting) authority, or NULL * if we have no such certificate. */ -authority_cert_t * -get_my_v3_authority_cert(void) +MOCK_IMPL(authority_cert_t *, +get_my_v3_authority_cert, (void)) { return authority_key_certificate; } @@ -683,7 +687,9 @@ router_initialize_tls_context(void) if (!lifetime) { /* we should guess a good ssl cert lifetime */ /* choose between 5 and 365 days, and round to the day */ - lifetime = 5*24*3600 + crypto_rand_int(361*24*3600); + unsigned int five_days = 5*24*3600; + unsigned int one_year = 365*24*3600; + lifetime = crypto_rand_int_range(five_days, one_year); lifetime -= lifetime % (24*3600); if (crypto_rand_int(2)) { @@ -761,6 +767,46 @@ router_write_fingerprint(int hashed) return result; } +static int +init_keys_common(void) +{ + if (!key_lock) + key_lock = tor_mutex_new(); + + /* There are a couple of paths that put us here before we've asked + * openssl to initialize itself. */ + if (crypto_global_init(get_options()->HardwareAccel, + get_options()->AccelName, + get_options()->AccelDir)) { + log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); + return -1; + } + + return 0; +} + +int +init_keys_client(void) +{ + crypto_pk_t *prkey; + if (init_keys_common() < 0) + return -1; + + if (!(prkey = crypto_pk_new())) + return -1; + if (crypto_pk_generate_key(prkey)) { + crypto_pk_free(prkey); + return -1; + } + set_client_identity_key(prkey); + /* Create a TLS context. */ + if (router_initialize_tls_context() < 0) { + log_err(LD_GENERAL,"Error creating TLS context for Tor client."); + return -1; + } + return 0; +} + /** Initialize all OR private keys, and the TLS context, as necessary. * On OPs, this only initializes the tls context. Return 0 on success, * or -1 if Tor should die. @@ -780,35 +826,13 @@ init_keys(void) int v3_digest_set = 0; authority_cert_t *cert = NULL; - if (!key_lock) - key_lock = tor_mutex_new(); - - /* There are a couple of paths that put us here before we've asked - * openssl to initialize itself. */ - if (crypto_global_init(get_options()->HardwareAccel, - get_options()->AccelName, - get_options()->AccelDir)) { - log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); - return -1; - } - /* OP's don't need persistent keys; just make up an identity and * initialize the TLS context. */ if (!server_mode(options)) { - if (!(prkey = crypto_pk_new())) - return -1; - if (crypto_pk_generate_key(prkey)) { - crypto_pk_free(prkey); - return -1; - } - set_client_identity_key(prkey); - /* Create a TLS context. */ - if (router_initialize_tls_context() < 0) { - log_err(LD_GENERAL,"Error creating TLS context for Tor client."); - return -1; - } - return 0; + return init_keys_client(); } + if (init_keys_common() < 0) + return -1; /* Make sure DataDirectory exists, and is private. */ if (check_private_dir(options->DataDirectory, CPD_CREATE, options->User)) { return -1; @@ -861,6 +885,10 @@ init_keys(void) set_client_identity_key(prkey); } + /* 1d. Load all ed25519 keys */ + if (load_ed_keys(options,now) < 0) + return -1; + /* 2. Read onion key. Make it if none is found. */ keydir = get_datadir_fname2("keys", "secret_onion_key"); log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir); @@ -926,6 +954,13 @@ init_keys(void) return -1; } + /* 3b. Get an ed25519 link certificate. Note that we need to do this + * after we set up the TLS context */ + if (generate_ed_link_cert(options, now) < 0) { + log_err(LD_GENERAL,"Couldn't make link cert"); + return -1; + } + /* 4. Build our router descriptor. */ /* Must be called after keys are initialized. */ mydesc = router_get_my_descriptor(); @@ -991,6 +1026,7 @@ init_keys(void) ds = trusted_dir_server_new(options->Nickname, NULL, router_get_advertised_dir_port(options, 0), router_get_advertised_or_port(options), + NULL, digest, v3_digest, type, 0.0); @@ -1043,63 +1079,93 @@ router_reset_reachability(void) can_reach_or_port = can_reach_dir_port = 0; } -/** Return 1 if ORPort is known reachable; else return 0. */ -int -check_whether_orport_reachable(void) +/** Return 1 if we won't do reachability checks, because: + * - AssumeReachable is set, or + * - the network is disabled. + * Otherwise, return 0. + */ +static int +router_reachability_checks_disabled(const or_options_t *options) { - const or_options_t *options = get_options(); return options->AssumeReachable || + net_is_disabled(); +} + +/** Return 0 if we need to do an ORPort reachability check, because: + * - no reachability check has been done yet, or + * - we've initiated reachability checks, but none have succeeded. + * Return 1 if we don't need to do an ORPort reachability check, because: + * - we've seen a successful reachability check, or + * - AssumeReachable is set, or + * - the network is disabled. + */ +int +check_whether_orport_reachable(const or_options_t *options) +{ + int reach_checks_disabled = router_reachability_checks_disabled(options); + return reach_checks_disabled || can_reach_or_port; } -/** Return 1 if we don't have a dirport configured, or if it's reachable. */ +/** Return 0 if we need to do a DirPort reachability check, because: + * - no reachability check has been done yet, or + * - we've initiated reachability checks, but none have succeeded. + * Return 1 if we don't need to do a DirPort reachability check, because: + * - we've seen a successful reachability check, or + * - there is no DirPort set, or + * - AssumeReachable is set, or + * - the network is disabled. + */ int -check_whether_dirport_reachable(void) +check_whether_dirport_reachable(const or_options_t *options) { - const or_options_t *options = get_options(); - return !options->DirPort_set || - options->AssumeReachable || - net_is_disabled() || + int reach_checks_disabled = router_reachability_checks_disabled(options) || + !options->DirPort_set; + return reach_checks_disabled || can_reach_dir_port; } -/** Look at a variety of factors, and return 0 if we don't want to - * advertise the fact that we have a DirPort open. Else return the - * DirPort we want to advertise. - * - * Log a helpful message if we change our mind about whether to publish - * a DirPort. +/** The lower threshold of remaining bandwidth required to advertise (or + * automatically provide) directory services */ +/* XXX Should this be increased? */ +#define MIN_BW_TO_ADVERTISE_DIRSERVER 51200 + +/** Return true iff we have enough configured bandwidth to cache directory + * information. */ +static int +router_has_bandwidth_to_be_dirserver(const or_options_t *options) +{ + if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) { + return 0; + } + if (options->RelayBandwidthRate > 0 && + options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRSERVER) { + return 0; + } + return 1; +} + +/** Helper: Return 1 if we have sufficient resources for serving directory + * requests, return 0 otherwise. + * dir_port is either 0 or the configured DirPort number. + * If AccountingMax is set less than our advertised bandwidth, then don't + * serve requests. Likewise, if our advertised bandwidth is less than + * MIN_BW_TO_ADVERTISE_DIRSERVER, don't bother trying to serve requests. */ static int -decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) +router_should_be_directory_server(const or_options_t *options, int dir_port) { static int advertising=1; /* start out assuming we will advertise */ int new_choice=1; const char *reason = NULL; - /* Section one: reasons to publish or not publish that aren't - * worth mentioning to the user, either because they're obvious - * or because they're normal behavior. */ - - if (!dir_port) /* short circuit the rest of the function */ - return 0; - if (authdir_mode(options)) /* always publish */ - return dir_port; - if (net_is_disabled()) - return 0; - if (!check_whether_dirport_reachable()) - return 0; - if (!router_get_advertised_dir_port(options, dir_port)) - return 0; - - /* Section two: reasons to publish or not publish that the user - * might find surprising. These are generally config options that - * make us choose not to publish. */ - - if (accounting_is_enabled(options)) { + if (accounting_is_enabled(options) && + get_options()->AccountingRule != ACCT_IN) { /* Don't spend bytes for directory traffic if we could end up hibernating, * but allow DirPort otherwise. Some people set AccountingMax because - * they're confused or to get statistics. */ + * they're confused or to get statistics. Directory traffic has a much + * larger effect on output than input so there is no reason to turn it + * off if using AccountingRule in. */ int interval_length = accounting_get_interval_length(); uint32_t effective_bw = get_effective_bwrate(options); uint64_t acc_bytes; @@ -1108,10 +1174,11 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) "seconds long. Raising to 1."); interval_length = 1; } - log_info(LD_GENERAL, "Calculating whether to disable dirport: effective " + log_info(LD_GENERAL, "Calculating whether to advertise %s: effective " "bwrate: %u, AccountingMax: "U64_FORMAT", " - "accounting interval length %d", effective_bw, - U64_PRINTF_ARG(options->AccountingMax), + "accounting interval length %d", + dir_port ? "dirport" : "begindir", + effective_bw, U64_PRINTF_ARG(options->AccountingMax), interval_length); acc_bytes = options->AccountingMax; @@ -1122,10 +1189,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) new_choice = 0; reason = "AccountingMax enabled"; } -#define MIN_BW_TO_ADVERTISE_DIRPORT 51200 - } else if (options->BandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT || - (options->RelayBandwidthRate > 0 && - options->RelayBandwidthRate < MIN_BW_TO_ADVERTISE_DIRPORT)) { + } else if (! router_has_bandwidth_to_be_dirserver(options)) { /* if we're advertising a small amount */ new_choice = 0; reason = "BandwidthRate under 50KB"; @@ -1133,15 +1197,91 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) if (advertising != new_choice) { if (new_choice == 1) { - log_notice(LD_DIR, "Advertising DirPort as %d", dir_port); + if (dir_port > 0) + log_notice(LD_DIR, "Advertising DirPort as %d", dir_port); + else + log_notice(LD_DIR, "Advertising directory service support"); } else { tor_assert(reason); - log_notice(LD_DIR, "Not advertising DirPort (Reason: %s)", reason); + log_notice(LD_DIR, "Not advertising Dir%s (Reason: %s)", + dir_port ? "Port" : "ectory Service support", reason); } advertising = new_choice; } - return advertising ? dir_port : 0; + return advertising; +} + +/** Return 1 if we are configured to accept either relay or directory requests + * from clients and we aren't at risk of exceeding our bandwidth limits, thus + * we should be a directory server. If not, return 0. + */ +int +dir_server_mode(const or_options_t *options) +{ + if (!options->DirCache) + return 0; + return options->DirPort_set || + (server_mode(options) && router_has_bandwidth_to_be_dirserver(options)); +} + +/** Look at a variety of factors, and return 0 if we don't want to + * advertise the fact that we have a DirPort open or begindir support, else + * return 1. + * + * Where dir_port or supports_tunnelled_dir_requests are not relevant, they + * must be 0. + * + * Log a helpful message if we change our mind about whether to publish. + */ +static int +decide_to_advertise_dir_impl(const or_options_t *options, + uint16_t dir_port, + int supports_tunnelled_dir_requests) +{ + /* Part one: reasons to publish or not publish that aren't + * worth mentioning to the user, either because they're obvious + * or because they're normal behavior. */ + + /* short circuit the rest of the function */ + if (!dir_port && !supports_tunnelled_dir_requests) + return 0; + if (authdir_mode(options)) /* always publish */ + return 1; + if (net_is_disabled()) + return 0; + if (dir_port && !router_get_advertised_dir_port(options, dir_port)) + return 0; + if (supports_tunnelled_dir_requests && + !router_get_advertised_or_port(options)) + return 0; + + /* Part two: consider config options that could make us choose to + * publish or not publish that the user might find surprising. */ + return router_should_be_directory_server(options, dir_port); +} + +/** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to + * advertise the fact that we have a DirPort open, else return the + * DirPort we want to advertise. + */ +static int +decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port) +{ + /* supports_tunnelled_dir_requests is not relevant, pass 0 */ + return decide_to_advertise_dir_impl(options, dir_port, 0) ? dir_port : 0; +} + +/** Front-end to decide_to_advertise_dir_impl(): return 0 if we don't want to + * advertise the fact that we support begindir requests, else return 1. + */ +static int +decide_to_advertise_begindir(const or_options_t *options, + int supports_tunnelled_dir_requests) +{ + /* dir_port is not relevant, pass 0 */ + return decide_to_advertise_dir_impl(options, 0, + supports_tunnelled_dir_requests); } /** Allocate and return a new extend_info_t that can be used to build @@ -1175,9 +1315,9 @@ void consider_testing_reachability(int test_or, int test_dir) { const routerinfo_t *me = router_get_my_routerinfo(); - int orport_reachable = check_whether_orport_reachable(); - tor_addr_t addr; const or_options_t *options = get_options(); + int orport_reachable = check_whether_orport_reachable(options); + tor_addr_t addr; if (!me) return; @@ -1208,14 +1348,15 @@ consider_testing_reachability(int test_or, int test_dir) extend_info_free(ei); } + /* XXX IPv6 self testing */ tor_addr_from_ipv4h(&addr, me->addr); - if (test_dir && !check_whether_dirport_reachable() && + if (test_dir && !check_whether_dirport_reachable(options) && !connection_get_by_type_addr_port_purpose( CONN_TYPE_DIR, &addr, me->dir_port, DIR_PURPOSE_FETCH_SERVERDESC)) { /* ask myself, via tor, for my server descriptor. */ - directory_initiate_command(&addr, - me->or_port, me->dir_port, + directory_initiate_command(&addr, me->or_port, + &addr, me->dir_port, me->cache_info.identity_digest, DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_GENERAL, @@ -1228,17 +1369,19 @@ void router_orport_found_reachable(void) { const routerinfo_t *me = router_get_my_routerinfo(); + const or_options_t *options = get_options(); if (!can_reach_or_port && me) { char *address = tor_dup_ip(me->addr); log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from " "the outside. Excellent.%s", - get_options()->PublishServerDescriptor_ != NO_DIRINFO ? + options->PublishServerDescriptor_ != NO_DIRINFO + && check_whether_dirport_reachable(options) ? " Publishing server descriptor." : ""); can_reach_or_port = 1; mark_my_descriptor_dirty("ORPort found reachable"); /* This is a significant enough change to upload immediately, * at least in a test network */ - if (get_options()->TestingTorNetwork == 1) { + if (options->TestingTorNetwork == 1) { reschedule_descriptor_update_check(); } control_event_server_status(LOG_NOTICE, @@ -1253,16 +1396,20 @@ void router_dirport_found_reachable(void) { const routerinfo_t *me = router_get_my_routerinfo(); + const or_options_t *options = get_options(); if (!can_reach_dir_port && me) { char *address = tor_dup_ip(me->addr); log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " - "from the outside. Excellent."); + "from the outside. Excellent.%s", + options->PublishServerDescriptor_ != NO_DIRINFO + && check_whether_orport_reachable(options) ? + " Publishing server descriptor." : ""); can_reach_dir_port = 1; - if (decide_to_advertise_dirport(get_options(), me->dir_port)) { + if (decide_to_advertise_dirport(options, me->dir_port)) { mark_my_descriptor_dirty("DirPort found reachable"); /* This is a significant enough change to upload immediately, * at least in a test network */ - if (get_options()->TestingTorNetwork == 1) { + if (options->TestingTorNetwork == 1) { reschedule_descriptor_update_check(); } } @@ -1421,8 +1568,8 @@ static int server_is_advertised=0; /** Return true iff we have published our descriptor lately. */ -int -advertised_server_mode(void) +MOCK_IMPL(int, +advertised_server_mode,(void)) { return server_is_advertised; } @@ -1459,7 +1606,10 @@ proxy_mode(const or_options_t *options) * and * - We have ORPort set * and - * - We believe we are reachable from the outside; or + * - We believe our ORPort and DirPort (if present) are reachable from + * the outside; or + * - We believe our ORPort is reachable from the outside, and we can't + * check our DirPort because the consensus has no exits; or * - We are an authoritative directory server. */ static int @@ -1477,8 +1627,15 @@ decide_if_publishable_server(void) return 1; if (!router_get_advertised_or_port(options)) return 0; - - return check_whether_orport_reachable(); + if (!check_whether_orport_reachable(options)) + return 0; + if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) { + /* All set: there are no exits in the consensus (maybe this is a tiny + * test network), so we can't check our DirPort reachability. */ + return 1; + } else { + return check_whether_dirport_reachable(options); + } } /** Initiate server descriptor upload as reasonable (if server is publishable, @@ -1649,7 +1806,8 @@ router_upload_dir_desc_to_dirservers(int force) int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port) { - if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */ + const routerinfo_t *me = router_get_my_routerinfo(); + if (!me) /* make sure routerinfo exists */ return -1; /* make sure it's resolved to something. this way we can't get a @@ -1657,20 +1815,21 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port) if (tor_addr_is_null(addr)) return -1; - /* look at desc_routerinfo->exit_policy for both the v4 and the v6 - * policies. The exit_policy field in desc_routerinfo is a bit unusual, - * in that it contains IPv6 and IPv6 entries. We don't want to look - * at desc_routerinfio->ipv6_exit_policy, since that's a port summary. */ + /* look at router_get_my_routerinfo()->exit_policy for both the v4 and the + * v6 policies. The exit_policy field in router_get_my_routerinfo() is a + * bit unusual, in that it contains IPv6 and IPv6 entries. We don't want to + * look at router_get_my_routerinfo()->ipv6_exit_policy, since that's a port + * summary. */ if ((tor_addr_family(addr) == AF_INET || tor_addr_family(addr) == AF_INET6)) { return compare_tor_addr_to_addr_policy(addr, port, - desc_routerinfo->exit_policy) != ADDR_POLICY_ACCEPTED; + me->exit_policy) != ADDR_POLICY_ACCEPTED; #if 0 } else if (tor_addr_family(addr) == AF_INET6) { return get_options()->IPv6Exit && desc_routerinfo->ipv6_exit_policy && compare_tor_addr_to_short_policy(addr, port, - desc_routerinfo->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED; + me->ipv6_exit_policy) != ADDR_POLICY_ACCEPTED; #endif } else { return -1; @@ -1679,13 +1838,13 @@ router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port) /** Return true iff my exit policy is reject *:*. Return -1 if we don't * have a descriptor */ -int -router_my_exit_policy_is_reject_star(void) +MOCK_IMPL(int, +router_my_exit_policy_is_reject_star,(void)) { - if (!router_get_my_routerinfo()) /* make sure desc_routerinfo exists */ + if (!router_get_my_routerinfo()) /* make sure routerinfo exists */ return -1; - return desc_routerinfo->policy_is_reject_star; + return router_get_my_routerinfo()->policy_is_reject_star; } /** Return true iff I'm a server and <b>digest</b> is equal to @@ -1744,12 +1903,13 @@ const char * router_get_my_descriptor(void) { const char *body; - if (!router_get_my_routerinfo()) + const routerinfo_t *me = router_get_my_routerinfo(); + if (! me) return NULL; + tor_assert(me->cache_info.saved_location == SAVED_NOWHERE); + body = signed_descriptor_get_body(&me->cache_info); /* Make sure this is nul-terminated. */ - tor_assert(desc_routerinfo->cache_info.saved_location == SAVED_NOWHERE); - body = signed_descriptor_get_body(&desc_routerinfo->cache_info); - tor_assert(!body[desc_routerinfo->cache_info.signed_descriptor_len]); + tor_assert(!body[me->cache_info.signed_descriptor_len]); log_debug(LD_GENERAL,"my desc is '%s'", body); return body; } @@ -1784,8 +1944,8 @@ static int router_guess_address_from_dir_headers(uint32_t *guess); * it's configured in torrc, or because we've learned it from * dirserver headers. Place the answer in *<b>addr</b> and return * 0 on success, else return -1 if we have no guess. */ -int -router_pick_published_address(const or_options_t *options, uint32_t *addr) +MOCK_IMPL(int, +router_pick_published_address,(const or_options_t *options, uint32_t *addr)) { *addr = get_last_resolved_addr(); if (!*addr && @@ -1802,12 +1962,15 @@ router_pick_published_address(const or_options_t *options, uint32_t *addr) return 0; } -/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh - * routerinfo, signed server descriptor, and extra-info document for this OR. - * Return 0 on success, -1 on temporary error. +/** Build a fresh routerinfo, signed server descriptor, and extra-info document + * for this OR. Set r to the generated routerinfo, e to the generated + * extra-info document. Return 0 on success, -1 on temporary error. Failure to + * generate an extra-info document is not an error and is indicated by setting + * e to NULL. Caller is responsible for freeing generated documents if 0 is + * returned. */ int -router_rebuild_descriptor(int force) +router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) { routerinfo_t *ri; extrainfo_t *ei; @@ -1816,26 +1979,19 @@ router_rebuild_descriptor(int force) int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); - if (desc_clean_since && !force) - return 0; - - if (router_pick_published_address(options, &addr) < 0 || - router_get_advertised_or_port(options) == 0) { - /* Stop trying to rebuild our descriptor every second. We'll - * learn that it's time to try again when ip_address_changed() - * marks it dirty. */ - desc_clean_since = time(NULL); + if (router_pick_published_address(options, &addr) < 0) { + log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); return -1; } - log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); - ri = tor_malloc_zero(sizeof(routerinfo_t)); ri->cache_info.routerlist_index = -1; ri->nickname = tor_strdup(options->Nickname); ri->addr = addr; ri->or_port = router_get_advertised_or_port(options); ri->dir_port = router_get_advertised_dir_port(options, 0); + ri->supports_tunnelled_dir_requests = + directory_permits_begindir_requests(options); ri->cache_info.published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); /* must invoke from * main thread */ @@ -1851,7 +2007,11 @@ router_rebuild_descriptor(int force) ! p->server_cfg.no_advertise && ! p->server_cfg.bind_ipv4_only && tor_addr_family(&p->addr) == AF_INET6) { - if (! tor_addr_is_internal(&p->addr, 0)) { + /* Like IPv4, if the relay is configured using the default + * authorities, disallow internal IPs. Otherwise, allow them. */ + const int default_auth = (!options->DirAuthorities && + !options->AlternateDirAuthority); + if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) { ipv6_orport = p; break; } else { @@ -1859,7 +2019,7 @@ router_rebuild_descriptor(int force) log_warn(LD_CONFIG, "Unable to use configured IPv6 address \"%s\" in a " "descriptor. Skipping it. " - "Try specifying a globally reachable address explicitly. ", + "Try specifying a globally reachable address explicitly.", tor_addr_to_str(addrbuf, &p->addr, sizeof(addrbuf), 1)); } } @@ -1876,6 +2036,9 @@ router_rebuild_descriptor(int force) routerinfo_free(ri); return -1; } + ri->cache_info.signing_key_cert = + tor_cert_dup(get_master_signing_key_cert()); + get_platform_str(platform, sizeof(platform)); ri->platform = tor_strdup(platform); @@ -1891,7 +2054,7 @@ router_rebuild_descriptor(int force) /* DNS is screwed up; don't claim to be an exit. */ policies_exit_policy_append_reject_star(&ri->exit_policy); } else { - policies_parse_exit_policy_from_options(options,ri->addr, + policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr, &ri->exit_policy); } ri->policy_is_reject_star = @@ -1966,10 +2129,14 @@ router_rebuild_descriptor(int force) ei->cache_info.is_extrainfo = 1; strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname)); ei->cache_info.published_on = ri->cache_info.published_on; + ei->cache_info.signing_key_cert = + tor_cert_dup(get_master_signing_key_cert()); + memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest, DIGEST_LEN); if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body, - ei, get_server_identity_key()) < 0) { + ei, get_server_identity_key(), + get_master_signing_keypair()) < 0) { log_warn(LD_BUG, "Couldn't generate extra-info descriptor."); extrainfo_free(ei); ei = NULL; @@ -1979,6 +2146,10 @@ router_rebuild_descriptor(int force) router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body, ei->cache_info.signed_descriptor_len, ei->cache_info.signed_descriptor_digest); + crypto_digest256((char*) ei->digest256, + ei->cache_info.signed_descriptor_body, + ei->cache_info.signed_descriptor_len, + DIGEST_SHA256); } /* Now finish the router descriptor. */ @@ -1986,12 +2157,18 @@ router_rebuild_descriptor(int force) memcpy(ri->cache_info.extra_info_digest, ei->cache_info.signed_descriptor_digest, DIGEST_LEN); + memcpy(ri->cache_info.extra_info_digest256, + ei->digest256, + DIGEST256_LEN); } else { /* ri was allocated with tor_malloc_zero, so there is no need to * zero ri->cache_info.extra_info_digest here. */ } - if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string( - ri, get_server_identity_key()))) { + if (! (ri->cache_info.signed_descriptor_body = + router_dump_router_to_string(ri, get_server_identity_key(), + get_onion_key(), + get_current_curve25519_keypair(), + get_master_signing_keypair())) ) { log_warn(LD_BUG, "Couldn't generate router descriptor."); routerinfo_free(ri); extrainfo_free(ei); @@ -2021,7 +2198,44 @@ router_rebuild_descriptor(int force) ri->cache_info.signed_descriptor_digest); if (ei) { - tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL)); + tor_assert(! + routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, + &ri->cache_info, NULL)); + } + + *r = ri; + *e = ei; + return 0; +} + +/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh + * routerinfo, signed server descriptor, and extra-info document for this OR. + * Return 0 on success, -1 on temporary error. + */ +int +router_rebuild_descriptor(int force) +{ + routerinfo_t *ri; + extrainfo_t *ei; + uint32_t addr; + const or_options_t *options = get_options(); + + if (desc_clean_since && !force) + return 0; + + if (router_pick_published_address(options, &addr) < 0 || + router_get_advertised_or_port(options) == 0) { + /* Stop trying to rebuild our descriptor every second. We'll + * learn that it's time to try again when ip_address_changed() + * marks it dirty. */ + desc_clean_since = time(NULL); + return -1; + } + + log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); + + if (router_build_fresh_descriptor(&ri, &ei) < 0) { + return -1; } routerinfo_free(desc_routerinfo); @@ -2107,10 +2321,10 @@ check_descriptor_bandwidth_changed(time_t now) { static time_t last_changed = 0; uint64_t prev, cur; - if (!desc_routerinfo) + if (!router_get_my_routerinfo()) return; - prev = desc_routerinfo->bandwidthcapacity; + prev = router_get_my_routerinfo()->bandwidthcapacity; cur = we_are_hibernating() ? 0 : rep_hist_bandwidth_assess(); if ((prev != cur && (!prev || !cur)) || cur > prev*2 || @@ -2164,11 +2378,11 @@ check_descriptor_ipaddress_changed(time_t now) (void) now; - if (!desc_routerinfo) + if (router_get_my_routerinfo() == NULL) return; /* XXXX ipv6 */ - prev = desc_routerinfo->addr; + prev = router_get_my_routerinfo()->addr; if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) { log_info(LD_CONFIG,"options->Address didn't resolve into an IP."); return; @@ -2240,7 +2454,7 @@ router_new_address_suggestion(const char *suggestion, if (tor_addr_eq(&d_conn->base_.addr, &addr)) { /* Don't believe anybody who says our IP is their IP. */ log_debug(LD_DIR, "A directory server told us our IP address is %s, " - "but he's just reporting his own IP address. Ignoring.", + "but they are just reporting their own IP address. Ignoring.", suggestion); return; } @@ -2297,22 +2511,29 @@ get_platform_str(char *platform, size_t len) */ char * router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key) + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, + const ed25519_keypair_t *signing_keypair) { char *address = NULL; char *onion_pkey = NULL; /* Onion key, PEM-encoded. */ char *identity_pkey = NULL; /* Identity key, PEM-encoded. */ - char digest[DIGEST_LEN]; + char digest[DIGEST256_LEN]; char published[ISO_TIME_LEN+1]; char fingerprint[FINGERPRINT_LEN+1]; - int has_extra_info_digest; - char extra_info_digest[HEX_DIGEST_LEN+1]; + char *extra_info_line = NULL; size_t onion_pkeylen, identity_pkeylen; char *family_line = NULL; char *extra_or_address = NULL; const or_options_t *options = get_options(); smartlist_t *chunks = NULL; char *output = NULL; + const int emit_ed_sigs = signing_keypair && + router->cache_info.signing_key_cert; + char *ed_cert_line = NULL; + char *rsa_tap_cc_line = NULL; + char *ntor_cc_line = NULL; /* Make sure the identity key matches the one in the routerinfo. */ if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) { @@ -2320,6 +2541,16 @@ router_dump_router_to_string(routerinfo_t *router, "match router's public key!"); goto err; } + if (emit_ed_sigs) { + if (!router->cache_info.signing_key_cert->signing_key_included || + !ed25519_pubkey_eq(&router->cache_info.signing_key_cert->signed_key, + &signing_keypair->pubkey)) { + log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched " + "ed25519 key chain %d", + router->cache_info.signing_key_cert->signing_key_included); + goto err; + } + } /* record our fingerprint, so we can include it in the descriptor */ if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) { @@ -2327,6 +2558,30 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + if (emit_ed_sigs) { + /* Encode ed25519 signing cert */ + char ed_cert_base64[256]; + char ed_fp_base64[ED25519_BASE64_LEN+1]; + if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64), + (const char*)router->cache_info.signing_key_cert->encoded, + router->cache_info.signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG,"Couldn't base64-encode signing key certificate!"); + goto err; + } + if (ed25519_public_to_base64(ed_fp_base64, + &router->cache_info.signing_key_cert->signing_key)<0) { + log_err(LD_BUG,"Couldn't base64-encode identity key\n"); + goto err; + } + tor_asprintf(&ed_cert_line, "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n" + "master-key-ed25519 %s\n", + ed_cert_base64, ed_fp_base64); + } + /* PEM-encode the onion key */ if (crypto_pk_write_public_key_to_string(router->onion_pkey, &onion_pkey,&onion_pkeylen)<0) { @@ -2341,6 +2596,69 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + /* Cross-certify with RSA key */ + if (tap_key && router->cache_info.signing_key_cert && + router->cache_info.signing_key_cert->signing_key_included) { + char buf[256]; + int tap_cc_len = 0; + uint8_t *tap_cc = + make_tap_onion_key_crosscert(tap_key, + &router->cache_info.signing_key_cert->signing_key, + router->identity_pkey, + &tap_cc_len); + if (!tap_cc) { + log_warn(LD_BUG,"make_tap_onion_key_crosscert failed!"); + goto err; + } + + if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!"); + tor_free(tap_cc); + goto err; + } + tor_free(tap_cc); + + tor_asprintf(&rsa_tap_cc_line, + "onion-key-crosscert\n" + "-----BEGIN CROSSCERT-----\n" + "%s" + "-----END CROSSCERT-----\n", buf); + } + + /* Cross-certify with onion keys */ + if (ntor_keypair && router->cache_info.signing_key_cert && + router->cache_info.signing_key_cert->signing_key_included) { + int sign = 0; + char buf[256]; + /* XXXX Base the expiration date on the actual onion key expiration time?*/ + tor_cert_t *cert = + make_ntor_onion_key_crosscert(ntor_keypair, + &router->cache_info.signing_key_cert->signing_key, + router->cache_info.published_on, + MIN_ONION_KEY_LIFETIME, &sign); + if (!cert) { + log_warn(LD_BUG,"make_ntor_onion_key_crosscert failed!"); + goto err; + } + tor_assert(sign == 0 || sign == 1); + + if (base64_encode(buf, sizeof(buf), + (const char*)cert->encoded, cert->encoded_len, + BASE64_ENCODE_MULTILINE)<0) { + log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!"); + tor_cert_free(cert); + goto err; + } + tor_cert_free(cert); + + tor_asprintf(&ntor_cc_line, + "ntor-onion-key-crosscert %d\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", sign, buf); + } + /* Encode the publication time. */ format_iso_time(published, router->cache_info.published_on); @@ -2353,12 +2671,19 @@ router_dump_router_to_string(routerinfo_t *router, family_line = tor_strdup(""); } - has_extra_info_digest = - ! tor_digest_is_zero(router->cache_info.extra_info_digest); - - if (has_extra_info_digest) { + if (!tor_digest_is_zero(router->cache_info.extra_info_digest)) { + char extra_info_digest[HEX_DIGEST_LEN+1]; base16_encode(extra_info_digest, sizeof(extra_info_digest), router->cache_info.extra_info_digest, DIGEST_LEN); + if (!tor_digest256_is_zero(router->cache_info.extra_info_digest256)) { + char d256_64[BASE64_DIGEST256_LEN+1]; + digest256_to_base64(d256_64, router->cache_info.extra_info_digest256); + tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n", + extra_info_digest, d256_64); + } else { + tor_asprintf(&extra_info_line, "extra-info-digest %s\n", + extra_info_digest); + } } if (router->ipv6_orport && @@ -2380,20 +2705,23 @@ router_dump_router_to_string(routerinfo_t *router, smartlist_add_asprintf(chunks, "router %s %s %d 0 %d\n" "%s" + "%s" "platform %s\n" "protocols Link 1 2 Circuit 1\n" "published %s\n" "fingerprint %s\n" "uptime %ld\n" "bandwidth %d %d %d\n" - "%s%s%s%s" + "%s%s" "onion-key\n%s" "signing-key\n%s" + "%s%s" "%s%s%s%s", router->nickname, address, router->or_port, decide_to_advertise_dirport(options, router->dir_port), + ed_cert_line ? ed_cert_line : "", extra_or_address ? extra_or_address : "", router->platform, published, @@ -2402,15 +2730,15 @@ router_dump_router_to_string(routerinfo_t *router, (int) router->bandwidthrate, (int) router->bandwidthburst, (int) router->bandwidthcapacity, - has_extra_info_digest ? "extra-info-digest " : "", - has_extra_info_digest ? extra_info_digest : "", - has_extra_info_digest ? "\n" : "", + extra_info_line ? extra_info_line : "", (options->DownloadExtraInfo || options->V3AuthoritativeDir) ? "caches-extra-info\n" : "", onion_pkey, identity_pkey, + rsa_tap_cc_line ? rsa_tap_cc_line : "", + ntor_cc_line ? ntor_cc_line : "", family_line, we_are_hibernating() ? "hibernating 1\n" : "", - options->HidServDirectoryV2 ? "hidden-service-dir\n" : "", + "hidden-service-dir\n", options->AllowSingleHopExits ? "allow-single-hop-exits\n" : ""); if (options->ContactInfo && strlen(options->ContactInfo)) { @@ -2424,7 +2752,7 @@ router_dump_router_to_string(routerinfo_t *router, char kbuf[128]; base64_encode(kbuf, sizeof(kbuf), (const char *)router->onion_curve25519_pkey->public_key, - CURVE25519_PUBKEY_LEN); + CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); } @@ -2450,7 +2778,29 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(p6); } - /* Sign the descriptor */ + if (decide_to_advertise_begindir(options, + router->supports_tunnelled_dir_requests)) { + smartlist_add(chunks, tor_strdup("tunnelled-dir-server\n")); + } + + /* Sign the descriptor with Ed25519 */ + if (emit_ed_sigs) { + smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + crypto_digest_smartlist_prefix(digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + ed25519_signature_t sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + if (ed25519_signature_to_base64(buf, &sig) < 0) + goto err; + + smartlist_add_asprintf(chunks, "%s\n", buf); + } + + /* Sign the descriptor with RSA */ smartlist_add(chunks, tor_strdup("router-signature\n")); crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1); @@ -2502,6 +2852,10 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(onion_pkey); tor_free(identity_pkey); tor_free(extra_or_address); + tor_free(ed_cert_line); + tor_free(rsa_tap_cc_line); + tor_free(ntor_cc_line); + tor_free(extra_info_line); return output; } @@ -2516,44 +2870,13 @@ router_dump_exit_policy_to_string(const routerinfo_t *router, int include_ipv4, int include_ipv6) { - smartlist_t *exit_policy_strings; - char *policy_string = NULL; - if ((!router->exit_policy) || (router->policy_is_reject_star)) { return tor_strdup("reject *:*"); } - exit_policy_strings = smartlist_new(); - - SMARTLIST_FOREACH_BEGIN(router->exit_policy, addr_policy_t *, tmpe) { - char *pbuf; - int bytes_written_to_pbuf; - if ((tor_addr_family(&tmpe->addr) == AF_INET6) && (!include_ipv6)) { - continue; /* Don't include IPv6 parts of address policy */ - } - if ((tor_addr_family(&tmpe->addr) == AF_INET) && (!include_ipv4)) { - continue; /* Don't include IPv4 parts of address policy */ - } - - pbuf = tor_malloc(POLICY_BUF_LEN); - bytes_written_to_pbuf = policy_write_item(pbuf,POLICY_BUF_LEN, tmpe, 1); - - if (bytes_written_to_pbuf < 0) { - log_warn(LD_BUG, "router_dump_exit_policy_to_string ran out of room!"); - tor_free(pbuf); - goto done; - } - - smartlist_add(exit_policy_strings,pbuf); - } SMARTLIST_FOREACH_END(tmpe); - - policy_string = smartlist_join_strings(exit_policy_strings, "\n", 0, NULL); - - done: - SMARTLIST_FOREACH(exit_policy_strings, char *, str, tor_free(str)); - smartlist_free(exit_policy_strings); - - return policy_string; + return policy_dump_to_string(router->exit_policy, + include_ipv4, + include_ipv6); } /** Copy the primary (IPv4) OR port (IP address and TCP port) for @@ -2645,7 +2968,8 @@ load_stats_file(const char *filename, const char *end_line, time_t now, * success, negative on failure. */ int extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, - crypto_pk_t *ident_key) + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair) { const or_options_t *options = get_options(); char identity[HEX_DIGEST_LEN+1]; @@ -2655,20 +2979,47 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, int result; static int write_stats_to_extrainfo = 1; char sig[DIROBJ_MAX_SIG_LEN+1]; - char *s, *pre, *contents, *cp, *s_dup = NULL; + char *s = NULL, *pre, *contents, *cp, *s_dup = NULL; time_t now = time(NULL); smartlist_t *chunks = smartlist_new(); extrainfo_t *ei_tmp = NULL; + const int emit_ed_sigs = signing_keypair && + extrainfo->cache_info.signing_key_cert; + char *ed_cert_line = NULL; base16_encode(identity, sizeof(identity), extrainfo->cache_info.identity_digest, DIGEST_LEN); format_iso_time(published, extrainfo->cache_info.published_on); bandwidth_usage = rep_hist_get_bandwidth_lines(); + if (emit_ed_sigs) { + if (!extrainfo->cache_info.signing_key_cert->signing_key_included || + !ed25519_pubkey_eq(&extrainfo->cache_info.signing_key_cert->signed_key, + &signing_keypair->pubkey)) { + log_warn(LD_BUG, "Tried to sign a extrainfo descriptor with a " + "mismatched ed25519 key chain %d", + extrainfo->cache_info.signing_key_cert->signing_key_included); + goto err; + } + char ed_cert_base64[256]; + if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64), + (const char*)extrainfo->cache_info.signing_key_cert->encoded, + extrainfo->cache_info.signing_key_cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG,"Couldn't base64-encode signing key certificate!"); + goto err; + } + tor_asprintf(&ed_cert_line, "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----\n", ed_cert_base64); + } else { + ed_cert_line = tor_strdup(""); + } - tor_asprintf(&pre, "extra-info %s %s\npublished %s\n%s", + tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n%s", extrainfo->nickname, identity, + ed_cert_line, published, bandwidth_usage); - tor_free(bandwidth_usage); smartlist_add(chunks, pre); if (geoip_is_loaded(AF_INET)) @@ -2726,6 +3077,23 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, } } + if (emit_ed_sigs) { + char digest[DIGEST256_LEN]; + smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + crypto_digest_smartlist_prefix(digest, DIGEST256_LEN, + ED_DESC_SIGNATURE_PREFIX, + chunks, "", DIGEST_SHA256); + ed25519_signature_t sig; + char buf[ED25519_SIG_BASE64_LEN+1]; + if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN, + signing_keypair) < 0) + goto err; + if (ed25519_signature_to_base64(buf, &sig) < 0) + goto err; + + smartlist_add_asprintf(chunks, "%s\n", buf); + } + smartlist_add(chunks, tor_strdup("router-signature\n")); s = smartlist_join_strings(chunks, "", 0, NULL); @@ -2774,7 +3142,8 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, "adding statistics to this or any future " "extra-info descriptors."); write_stats_to_extrainfo = 0; - result = extrainfo_dump_to_string(s_out, extrainfo, ident_key); + result = extrainfo_dump_to_string(s_out, extrainfo, ident_key, + signing_keypair); goto done; } else { log_warn(LD_BUG, "We just generated an extrainfo descriptor we " @@ -2796,7 +3165,9 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); smartlist_free(chunks); tor_free(s_dup); + tor_free(ed_cert_line); extrainfo_free(ei_tmp); + tor_free(bandwidth_usage); return result; } @@ -3121,28 +3492,16 @@ router_free_all(void) /** Return a smartlist of tor_addr_port_t's with all the OR ports of <b>ri</b>. Note that freeing of the items in the list as well as - the smartlist itself is the callers responsibility. - - XXX duplicating code from node_get_all_orports(). */ + the smartlist itself is the callers responsibility. */ smartlist_t * router_get_all_orports(const routerinfo_t *ri) { - smartlist_t *sl = smartlist_new(); tor_assert(ri); - - if (ri->addr != 0) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_from_ipv4h(&ap->addr, ri->addr); - ap->port = ri->or_port; - smartlist_add(sl, ap); - } - if (!tor_addr_is_null(&ri->ipv6_addr)) { - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); - tor_addr_copy(&ap->addr, &ri->ipv6_addr); - ap->port = ri->or_port; - smartlist_add(sl, ap); - } - - return sl; + node_t fake_node; + memset(&fake_node, 0, sizeof(fake_node)); + /* we don't modify ri, fake_node is passed as a const node_t * + */ + fake_node.ri = (routerinfo_t *)ri; + return node_get_all_orports(&fake_node); } diff --git a/src/or/router.h b/src/or/router.h index 8108ffb22f..73bfea1faa 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -22,7 +22,7 @@ int server_identity_key_is_set(void); void set_client_identity_key(crypto_pk_t *k); crypto_pk_t *get_tlsclient_identity_key(void); int client_identity_key_is_set(void); -authority_cert_t *get_my_v3_authority_cert(void); +MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void)); crypto_pk_t *get_my_v3_authority_signing_key(void); authority_cert_t *get_my_v3_legacy_cert(void); crypto_pk_t *get_my_v3_legacy_signing_key(void); @@ -37,9 +37,11 @@ void ntor_key_map_free(di_digest256_map_t *map); int router_initialize_tls_context(void); int init_keys(void); +int init_keys_client(void); -int check_whether_orport_reachable(void); -int check_whether_dirport_reachable(void); +int check_whether_orport_reachable(const or_options_t *options); +int check_whether_dirport_reachable(const or_options_t *options); +int dir_server_mode(const or_options_t *options); void consider_testing_reachability(int test_or, int test_dir); void router_orport_found_reachable(void); void router_dirport_found_reachable(void); @@ -66,7 +68,7 @@ uint16_t router_get_advertised_dir_port(const or_options_t *options, MOCK_DECL(int, server_mode, (const or_options_t *options)); MOCK_DECL(int, public_server_mode, (const or_options_t *options)); -int advertised_server_mode(void); +MOCK_DECL(int, advertised_server_mode, (void)); int proxy_mode(const or_options_t *options); void consider_publishable_server(int force); int should_refuse_unknown_exits(const or_options_t *options); @@ -79,7 +81,7 @@ void check_descriptor_ipaddress_changed(time_t now); void router_new_address_suggestion(const char *suggestion, const dir_connection_t *d_conn); int router_compare_to_my_exit_policy(const tor_addr_t *addr, uint16_t port); -int router_my_exit_policy_is_reject_star(void); +MOCK_DECL(int, router_my_exit_policy_is_reject_star,(void)); MOCK_DECL(const routerinfo_t *, router_get_my_routerinfo, (void)); extrainfo_t *router_get_my_extrainfo(void); const char *router_get_my_descriptor(void); @@ -88,10 +90,15 @@ int router_digest_is_me(const char *digest); const uint8_t *router_get_my_id_digest(void); int router_extrainfo_digest_is_me(const char *digest); int router_is_me(const routerinfo_t *router); -int router_pick_published_address(const or_options_t *options, uint32_t *addr); +MOCK_DECL(int,router_pick_published_address,(const or_options_t *options, + uint32_t *addr)); +int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key); + const crypto_pk_t *ident_key, + const crypto_pk_t *tap_key, + const curve25519_keypair_t *ntor_keypair, + const ed25519_keypair_t *signing_keypair); char *router_dump_exit_policy_to_string(const routerinfo_t *router, int include_ipv4, int include_ipv6); @@ -106,7 +113,8 @@ int router_has_addr(const routerinfo_t *router, const tor_addr_t *addr); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo, - crypto_pk_t *ident_key); + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair); int is_legal_nickname(const char *s); int is_legal_nickname_or_hexdigest(const char *s); int is_legal_hexdigest(const char *s); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c new file mode 100644 index 0000000000..fba3491f2b --- /dev/null +++ b/src/or/routerkeys.c @@ -0,0 +1,1147 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file routerkeys.c + * + * \brief Functions and structures to handle generating and maintaining the + * set of keypairs necessary to be an OR. (Some of the code in router.c + * belongs here.) + */ + +#include "or.h" +#include "config.h" +#include "router.h" +#include "crypto_pwbox.h" +#include "routerkeys.h" +#include "torcert.h" + +#define ENC_KEY_HEADER "Boxed Ed25519 key" +#define ENC_KEY_TAG "master" + +static ssize_t +do_getpass(const char *prompt, char *buf, size_t buflen, + int twice, const or_options_t *options) +{ + if (options->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + tor_assert(buflen); + buf[0] = 0; + return 0; + } + + char *prompt2 = NULL; + char *buf2 = NULL; + int fd = -1; + ssize_t length = -1; + + if (options->use_keygen_passphrase_fd) { + twice = 0; + fd = options->keygen_passphrase_fd; + length = read_all(fd, buf, buflen-1, 0); + if (length >= 0) + buf[length] = 0; + goto done_reading; + } + + if (twice) { + const char msg[] = "One more time:"; + size_t p2len = strlen(prompt) + 1; + if (p2len < sizeof(msg)) + p2len = sizeof(msg); + prompt2 = tor_malloc(strlen(prompt)+1); + memset(prompt2, ' ', p2len); + memcpy(prompt2 + p2len - sizeof(msg), msg, sizeof(msg)); + + buf2 = tor_malloc_zero(buflen); + } + + while (1) { + length = tor_getpass(prompt, buf, buflen); + if (length < 0) + goto done_reading; + + if (! twice) + break; + + ssize_t length2 = tor_getpass(prompt2, buf2, buflen); + + if (length != length2 || tor_memneq(buf, buf2, length)) { + fprintf(stderr, "That didn't match.\n"); + } else { + break; + } + } + + done_reading: + if (twice) { + tor_free(prompt2); + memwipe(buf2, 0, buflen); + tor_free(buf2); + } + + if (options->keygen_force_passphrase == FORCE_PASSPHRASE_ON && length == 0) + return -1; + + return length; +} + +int +read_encrypted_secret_key(ed25519_secret_key_t *out, + const char *fname) +{ + int r = -1; + uint8_t *secret = NULL; + size_t secret_len = 0; + char pwbuf[256]; + uint8_t encrypted_key[256]; + char *tag = NULL; + int saved_errno = 0; + + ssize_t encrypted_len = crypto_read_tagged_contents_from_file(fname, + ENC_KEY_HEADER, + &tag, + encrypted_key, + sizeof(encrypted_key)); + if (encrypted_len < 0) { + saved_errno = errno; + log_info(LD_OR, "%s is missing", fname); + r = 0; + goto done; + } + if (strcmp(tag, ENC_KEY_TAG)) { + saved_errno = EINVAL; + goto done; + } + + while (1) { + ssize_t pwlen = + do_getpass("Enter pasphrase for master key:", pwbuf, sizeof(pwbuf), 0, + get_options()); + if (pwlen < 0) { + saved_errno = EINVAL; + goto done; + } + const int r = crypto_unpwbox(&secret, &secret_len, + encrypted_key, encrypted_len, + pwbuf, pwlen); + if (r == UNPWBOX_CORRUPTED) { + log_err(LD_OR, "%s is corrupted.", fname); + saved_errno = EINVAL; + goto done; + } else if (r == UNPWBOX_OKAY) { + break; + } + + /* Otherwise, passphrase is bad, so try again till user does ctrl-c or gets + * it right. */ + } + + if (secret_len != ED25519_SECKEY_LEN) { + log_err(LD_OR, "%s is corrupted.", fname); + saved_errno = EINVAL; + goto done; + } + memcpy(out->seckey, secret, ED25519_SECKEY_LEN); + r = 1; + + done: + memwipe(encrypted_key, 0, sizeof(encrypted_key)); + memwipe(pwbuf, 0, sizeof(pwbuf)); + tor_free(tag); + if (secret) { + memwipe(secret, 0, secret_len); + tor_free(secret); + } + if (saved_errno) + errno = saved_errno; + return r; +} + +int +write_encrypted_secret_key(const ed25519_secret_key_t *key, + const char *fname) +{ + int r = -1; + char pwbuf0[256]; + uint8_t *encrypted_key = NULL; + size_t encrypted_len = 0; + + if (do_getpass("Enter new passphrase:", pwbuf0, sizeof(pwbuf0), 1, + get_options()) < 0) { + log_warn(LD_OR, "NO/failed passphrase"); + return -1; + } + + if (strlen(pwbuf0) == 0) { + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_ON) + return -1; + else + return 0; + } + + if (crypto_pwbox(&encrypted_key, &encrypted_len, + key->seckey, sizeof(key->seckey), + pwbuf0, strlen(pwbuf0), 0) < 0) { + log_warn(LD_OR, "crypto_pwbox failed!?"); + goto done; + } + if (crypto_write_tagged_contents_to_file(fname, + ENC_KEY_HEADER, + ENC_KEY_TAG, + encrypted_key, encrypted_len) < 0) + goto done; + r = 1; + done: + if (encrypted_key) { + memwipe(encrypted_key, 0, encrypted_len); + tor_free(encrypted_key); + } + memwipe(pwbuf0, 0, sizeof(pwbuf0)); + return r; +} + +static int +write_secret_key(const ed25519_secret_key_t *key, int encrypted, + const char *fname, + const char *fname_tag, + const char *encrypted_fname) +{ + if (encrypted) { + int r = write_encrypted_secret_key(key, encrypted_fname); + if (r == 1) { + /* Success! */ + + /* Try to unlink the unencrypted key, if any existed before */ + if (strcmp(fname, encrypted_fname)) + unlink(fname); + return r; + } else if (r != 0) { + /* Unrecoverable failure! */ + return r; + } + + fprintf(stderr, "Not encrypting the secret key.\n"); + } + return ed25519_seckey_write_to_file(key, fname, fname_tag); +} + +/** + * Read an ed25519 key and associated certificates from files beginning with + * <b>fname</b>, with certificate type <b>cert_type</b>. On failure, return + * NULL; on success return the keypair. + * + * If INIT_ED_KEY_CREATE is set in <b>flags</b>, then create the key (and + * certificate if requested) if it doesn't exist, and save it to disk. + * + * If INIT_ED_KEY_NEEDCERT is set in <b>flags</b>, load/create a certificate + * too and store it in *<b>cert_out</b>. Fail if the cert can't be + * found/created. To create a certificate, <b>signing_key</b> must be set to + * the key that should sign it; <b>now</b> to the current time, and + * <b>lifetime</b> to the lifetime of the key. + * + * If INIT_ED_KEY_REPLACE is set in <b>flags</b>, then create and save new key + * whether we can read the old one or not. + * + * If INIT_ED_KEY_EXTRA_STRONG is set in <b>flags</b>, set the extra_strong + * flag when creating the secret key. + * + * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in <b>flags</b>, and + * we create a new certificate, create it with the signing key embedded. + * + * If INIT_ED_KEY_SPLIT is set in <b>flags</b>, and we create a new key, + * store the public key in a separate file from the secret key. + * + * If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a + * public key file but no secret key file, return successfully anyway. + * + * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not try to load a + * secret key unless no public key is found. Do not return a secret key. (but + * create and save one if needed). + * + * If INIT_ED_KEY_NO_LOAD_SECRET is set in <b>flags</b>, don't try to load + * a secret key, no matter what. + * + * If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key + * and consider encrypting any new secret key. + * + * If INIT_ED_KEY_NO_REPAIR is set, and there is any issue loading the keys + * from disk _other than their absence_ (full or partial), we do not try to + * replace them. + * + * If INIT_ED_KEY_SUGGEST_KEYGEN is set, have log messages about failures + * refer to the --keygen option. + * + * If INIT_ED_KEY_EXPLICIT_FNAME is set, use the provided file name for the + * secret key file, encrypted or not. + */ +ed25519_keypair_t * +ed_key_init_from_file(const char *fname, uint32_t flags, + int severity, + const ed25519_keypair_t *signing_key, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out) +{ + char *secret_fname = NULL; + char *encrypted_secret_fname = NULL; + char *public_fname = NULL; + char *cert_fname = NULL; + const char *loaded_secret_fname = NULL; + int created_pk = 0, created_sk = 0, created_cert = 0; + const int try_to_load = ! (flags & INIT_ED_KEY_REPLACE); + const int encrypt_key = !! (flags & INIT_ED_KEY_TRY_ENCRYPTED); + const int norepair = !! (flags & INIT_ED_KEY_NO_REPAIR); + const int split = !! (flags & INIT_ED_KEY_SPLIT); + const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET); + const int offline_secret = !! (flags & INIT_ED_KEY_OFFLINE_SECRET); + const int explicit_fname = !! (flags & INIT_ED_KEY_EXPLICIT_FNAME); + + /* we don't support setting both of these flags at once. */ + tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) != + (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)); + + char tag[8]; + tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type); + + tor_cert_t *cert = NULL; + char *got_tag = NULL; + ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); + + if (explicit_fname) { + secret_fname = tor_strdup(fname); + encrypted_secret_fname = tor_strdup(fname); + } else { + tor_asprintf(&secret_fname, "%s_secret_key", fname); + tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname); + } + tor_asprintf(&public_fname, "%s_public_key", fname); + tor_asprintf(&cert_fname, "%s_cert", fname); + + /* Try to read the secret key. */ + int have_secret = 0; + int load_secret = try_to_load && + !offline_secret && + (!omit_secret || file_status(public_fname)==FN_NOENT); + if (load_secret) { + int rv = ed25519_seckey_read_from_file(&keypair->seckey, + &got_tag, secret_fname); + if (rv == 0) { + have_secret = 1; + loaded_secret_fname = secret_fname; + tor_assert(got_tag); + } else { + if (errno != ENOENT && norepair) { + tor_log(severity, LD_OR, "Unable to read %s: %s", secret_fname, + strerror(errno)); + goto err; + } + } + } + + /* Should we try for an encrypted key? */ + int have_encrypted_secret_file = 0; + if (!have_secret && try_to_load && encrypt_key) { + int r = read_encrypted_secret_key(&keypair->seckey, + encrypted_secret_fname); + if (r > 0) { + have_secret = 1; + have_encrypted_secret_file = 1; + tor_free(got_tag); /* convince coverity we aren't leaking */ + got_tag = tor_strdup(tag); + loaded_secret_fname = encrypted_secret_fname; + } else if (errno != ENOENT && norepair) { + tor_log(severity, LD_OR, "Unable to read %s: %s", + encrypted_secret_fname, strerror(errno)); + goto err; + } + } else { + if (try_to_load) { + /* Check if it's there anyway, so we don't replace it. */ + if (file_status(encrypted_secret_fname) != FN_NOENT) + have_encrypted_secret_file = 1; + } + } + + if (have_secret) { + if (strcmp(got_tag, tag)) { + tor_log(severity, LD_OR, "%s has wrong tag", loaded_secret_fname); + goto err; + } + /* Derive the public key */ + if (ed25519_public_key_generate(&keypair->pubkey, &keypair->seckey)<0) { + tor_log(severity, LD_OR, "%s can't produce a public key", + loaded_secret_fname); + goto err; + } + } + + /* If we do split keys here, try to read the pubkey. */ + int found_public = 0; + if (try_to_load && (!have_secret || split)) { + ed25519_public_key_t pubkey_tmp; + tor_free(got_tag); + found_public = ed25519_pubkey_read_from_file(&pubkey_tmp, + &got_tag, public_fname) == 0; + if (!found_public && errno != ENOENT && norepair) { + tor_log(severity, LD_OR, "Unable to read %s: %s", public_fname, + strerror(errno)); + goto err; + } + if (found_public && strcmp(got_tag, tag)) { + tor_log(severity, LD_OR, "%s has wrong tag", public_fname); + goto err; + } + if (found_public) { + if (have_secret) { + /* If we have a secret key and we're reloading the public key, + * the key must match! */ + if (! ed25519_pubkey_eq(&keypair->pubkey, &pubkey_tmp)) { + tor_log(severity, LD_OR, "%s does not match %s! If you are trying " + "to restore from backup, make sure you didn't mix up the " + "key files. If you are absolutely sure that %s is the right " + "key for this relay, delete %s or move it out of the way.", + public_fname, loaded_secret_fname, + loaded_secret_fname, public_fname); + goto err; + } + } else { + /* We only have the public key; better use that. */ + tor_assert(split); + memcpy(&keypair->pubkey, &pubkey_tmp, sizeof(pubkey_tmp)); + } + } else { + /* We have no public key file, but we do have a secret key, make the + * public key file! */ + if (have_secret) { + if (ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) + < 0) { + tor_log(severity, LD_OR, "Couldn't repair %s", public_fname); + goto err; + } else { + tor_log(LOG_NOTICE, LD_OR, + "Found secret key but not %s. Regenerating.", + public_fname); + } + } + } + } + + /* If the secret key is absent and it's not allowed to be, fail. */ + if (!have_secret && found_public && + !(flags & INIT_ED_KEY_MISSING_SECRET_OK)) { + if (have_encrypted_secret_file) { + tor_log(severity, LD_OR, "We needed to load a secret key from %s, " + "but it was encrypted. Try 'tor --keygen' instead, so you " + "can enter the passphrase.", + secret_fname); + } else if (offline_secret) { + tor_log(severity, LD_OR, "We wanted to load a secret key from %s, " + "but you're keeping it offline. (OfflineMasterKey is set.)", + secret_fname); + } else { + tor_log(severity, LD_OR, "We needed to load a secret key from %s, " + "but couldn't find it. %s", secret_fname, + (flags & INIT_ED_KEY_SUGGEST_KEYGEN) ? + "If you're keeping your master secret key offline, you will " + "need to run 'tor --keygen' to generate new signing keys." : + "Did you forget to copy it over when you copied the rest of the " + "signing key material?"); + } + goto err; + } + + /* If it's absent, and we're not supposed to make a new keypair, fail. */ + if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE)) { + if (split) { + tor_log(severity, LD_OR, "No key found in %s or %s.", + secret_fname, public_fname); + } else { + tor_log(severity, LD_OR, "No key found in %s.", secret_fname); + } + goto err; + } + + /* If the secret key is absent, but the encrypted key would be present, + * that's an error */ + if (!have_secret && !found_public && have_encrypted_secret_file) { + tor_assert(!encrypt_key); + tor_log(severity, LD_OR, "Found an encrypted secret key, " + "but not public key file %s!", public_fname); + goto err; + } + + /* if it's absent, make a new keypair... */ + if (!have_secret && !found_public) { + tor_free(keypair); + keypair = ed_key_new(signing_key, flags, now, lifetime, + cert_type, &cert); + if (!keypair) { + tor_log(severity, LD_OR, "Couldn't create keypair"); + goto err; + } + created_pk = created_sk = created_cert = 1; + } + + /* Write it to disk if we're supposed to do with a new passphrase, or if + * we just created it. */ + if (created_sk || (have_secret && get_options()->change_key_passphrase)) { + if (write_secret_key(&keypair->seckey, + encrypt_key, + secret_fname, tag, encrypted_secret_fname) < 0 + || + (split && + ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) < 0) + || + (cert && + crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", + tag, cert->encoded, cert->encoded_len) < 0)) { + tor_log(severity, LD_OR, "Couldn't write keys or cert to file."); + goto err; + } + goto done; + } + + /* If we're not supposed to get a cert, we're done. */ + if (! (flags & INIT_ED_KEY_NEEDCERT)) + goto done; + + /* Read a cert. */ + tor_free(got_tag); + uint8_t certbuf[256]; + ssize_t cert_body_len = crypto_read_tagged_contents_from_file( + cert_fname, "ed25519v1-cert", + &got_tag, certbuf, sizeof(certbuf)); + if (cert_body_len >= 0 && !strcmp(got_tag, tag)) + cert = tor_cert_parse(certbuf, cert_body_len); + + /* If we got it, check it to the extent we can. */ + int bad_cert = 0; + + if (! cert) { + tor_log(severity, LD_OR, "Cert was unparseable"); + bad_cert = 1; + } else if (!tor_memeq(cert->signed_key.pubkey, keypair->pubkey.pubkey, + ED25519_PUBKEY_LEN)) { + tor_log(severity, LD_OR, "Cert was for wrong key"); + bad_cert = 1; + } else if (signing_key && + tor_cert_checksig(cert, &signing_key->pubkey, now) < 0) { + tor_log(severity, LD_OR, "Can't check certificate"); + bad_cert = 1; + } else if (cert->cert_expired) { + tor_log(severity, LD_OR, "Certificate is expired"); + bad_cert = 1; + } else if (signing_key && cert->signing_key_included && + ! ed25519_pubkey_eq(&signing_key->pubkey, &cert->signing_key)) { + tor_log(severity, LD_OR, "Certificate signed by unexpectd key!"); + bad_cert = 1; + } + + if (bad_cert) { + tor_cert_free(cert); + cert = NULL; + } + + /* If we got a cert, we're done. */ + if (cert) + goto done; + + /* If we didn't get a cert, and we're not supposed to make one, fail. */ + if (!signing_key || !(flags & INIT_ED_KEY_CREATE)) { + tor_log(severity, LD_OR, "Without signing key, can't create certificate"); + goto err; + } + + /* We have keys but not a certificate, so make one. */ + uint32_t cert_flags = 0; + if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT) + cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY; + cert = tor_cert_create(signing_key, cert_type, + &keypair->pubkey, + now, lifetime, + cert_flags); + + if (! cert) { + tor_log(severity, LD_OR, "Couldn't create certificate"); + goto err; + } + + /* Write it to disk. */ + created_cert = 1; + if (crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert", + tag, cert->encoded, cert->encoded_len) < 0) { + tor_log(severity, LD_OR, "Couldn't write cert to disk."); + goto err; + } + + done: + if (cert_out) + *cert_out = cert; + else + tor_cert_free(cert); + + goto cleanup; + + err: + if (keypair) + memwipe(keypair, 0, sizeof(*keypair)); + tor_free(keypair); + tor_cert_free(cert); + if (cert_out) + *cert_out = NULL; + if (created_sk) + unlink(secret_fname); + if (created_pk) + unlink(public_fname); + if (created_cert) + unlink(cert_fname); + + cleanup: + tor_free(encrypted_secret_fname); + tor_free(secret_fname); + tor_free(public_fname); + tor_free(cert_fname); + tor_free(got_tag); + + return keypair; +} + +/** + * Create a new signing key and (optionally) certficiate; do not read or write + * from disk. See ed_key_init_from_file() for more information. + */ +ed25519_keypair_t * +ed_key_new(const ed25519_keypair_t *signing_key, + uint32_t flags, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out) +{ + if (cert_out) + *cert_out = NULL; + + const int extra_strong = !! (flags & INIT_ED_KEY_EXTRA_STRONG); + ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t)); + if (ed25519_keypair_generate(keypair, extra_strong) < 0) + goto err; + + if (! (flags & INIT_ED_KEY_NEEDCERT)) + return keypair; + + tor_assert(signing_key); + tor_assert(cert_out); + uint32_t cert_flags = 0; + if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT) + cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY; + tor_cert_t *cert = tor_cert_create(signing_key, cert_type, + &keypair->pubkey, + now, lifetime, + cert_flags); + if (! cert) + goto err; + + *cert_out = cert; + return keypair; + + err: + tor_free(keypair); + return NULL; +} + +static ed25519_keypair_t *master_identity_key = NULL; +static ed25519_keypair_t *master_signing_key = NULL; +static ed25519_keypair_t *current_auth_key = NULL; +static tor_cert_t *signing_key_cert = NULL; +static tor_cert_t *link_cert_cert = NULL; +static tor_cert_t *auth_key_cert = NULL; + +static uint8_t *rsa_ed_crosscert = NULL; +static size_t rsa_ed_crosscert_len = 0; + +/** + * Running as a server: load, reload, or refresh our ed25519 keys and + * certificates, creating and saving new ones as needed. + */ +int +load_ed_keys(const or_options_t *options, time_t now) +{ + ed25519_keypair_t *id = NULL; + ed25519_keypair_t *sign = NULL; + ed25519_keypair_t *auth = NULL; + const ed25519_keypair_t *sign_signing_key_with_id = NULL; + const ed25519_keypair_t *use_signing = NULL; + const tor_cert_t *check_signing_cert = NULL; + tor_cert_t *sign_cert = NULL; + tor_cert_t *auth_cert = NULL; + +#define FAIL(msg) do { \ + log_warn(LD_OR, (msg)); \ + goto err; \ + } while (0) +#define SET_KEY(key, newval) do { \ + if ((key) != (newval)) \ + ed25519_keypair_free(key); \ + key = (newval); \ + } while (0) +#define SET_CERT(cert, newval) do { \ + if ((cert) != (newval)) \ + tor_cert_free(cert); \ + cert = (newval); \ + } while (0) +#define EXPIRES_SOON(cert, interval) \ + (!(cert) || (cert)->valid_until < now + (interval)) + + /* XXXX support encrypted identity keys fully */ + + /* First try to get the signing key to see how it is. */ + { + char *fname = + options_get_datadir_fname2(options, "keys", "ed25519_signing"); + sign = ed_key_init_from_file( + fname, + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT, + LOG_INFO, + NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert); + tor_free(fname); + check_signing_cert = sign_cert; + use_signing = sign; + } + + if (!use_signing && master_signing_key) { + check_signing_cert = signing_key_cert; + use_signing = master_signing_key; + } + + const int offline_master = + options->OfflineMasterKey && options->command != CMD_KEYGEN; + const int need_new_signing_key = + NULL == use_signing || + EXPIRES_SOON(check_signing_cert, 0) || + (options->command == CMD_KEYGEN && ! options->change_key_passphrase); + const int want_new_signing_key = + need_new_signing_key || + EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); + + /* We can only create a master key if we haven't been told that the + * master key will always be offline. Also, if we have a signing key, + * then we shouldn't make a new master ID key. */ + const int can_make_master_id_key = !offline_master && + NULL == use_signing; + + if (need_new_signing_key) { + log_notice(LD_OR, "It looks like I need to generate and sign a new " + "medium-term signing key, because %s. To do that, I need to " + "load%s the permanent master identity key.", + (NULL == use_signing) ? "I don't have one" : + EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" : + "you asked me to make one with --keygen", + can_make_master_id_key ? " (or create)" : ""); + } else if (want_new_signing_key && !offline_master) { + log_notice(LD_OR, "It looks like I should try to generate and sign a " + "new medium-term signing key, because the one I have is " + "going to expire soon. To do that, I'm going to have to try to " + "load the permanent master identity key."); + } else if (want_new_signing_key) { + log_notice(LD_OR, "It looks like I should try to generate and sign a " + "new medium-term signing key, because the one I have is " + "going to expire soon. But OfflineMasterKey is set, so I " + "won't try to load a permanent master identity key is set. " + "You will need to use 'tor --keygen' make a new signing key " + "and certificate."); + } + + { + uint32_t flags = + (INIT_ED_KEY_SPLIT| + INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR); + if (can_make_master_id_key) + flags |= INIT_ED_KEY_CREATE; + if (! need_new_signing_key) + flags |= INIT_ED_KEY_MISSING_SECRET_OK; + if (! want_new_signing_key || offline_master) + flags |= INIT_ED_KEY_OMIT_SECRET; + if (offline_master) + flags |= INIT_ED_KEY_OFFLINE_SECRET; + if (options->command == CMD_KEYGEN) + flags |= INIT_ED_KEY_TRY_ENCRYPTED; + + /* Check the key directory */ + if (check_private_dir(options->DataDirectory, CPD_CREATE, options->User)) { + log_err(LD_OR, "Can't create/check datadirectory %s", + options->DataDirectory); + goto err; + } + char *fname = get_datadir_fname("keys"); + if (check_private_dir(fname, CPD_CREATE, options->User) < 0) { + log_err(LD_OR, "Problem creating/checking key directory %s", fname); + tor_free(fname); + goto err; + } + tor_free(fname); + if (options->master_key_fname) { + fname = tor_strdup(options->master_key_fname); + flags |= INIT_ED_KEY_EXPLICIT_FNAME; + } else { + fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id"); + } + id = ed_key_init_from_file( + fname, + flags, + LOG_WARN, NULL, 0, 0, 0, NULL); + tor_free(fname); + if (!id) { + if (need_new_signing_key) { + if (offline_master) + FAIL("Can't load master identity key; OfflineMasterKey is set."); + else + FAIL("Missing identity key"); + } else { + log_warn(LD_OR, "Master public key was absent; inferring from " + "public key in signing certificate and saving to disk."); + tor_assert(check_signing_cert); + id = tor_malloc_zero(sizeof(*id)); + memcpy(&id->pubkey, &check_signing_cert->signing_key, + sizeof(ed25519_public_key_t)); + fname = options_get_datadir_fname2(options, "keys", + "ed25519_master_id_public_key"); + if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) { + log_warn(LD_OR, "Error while attempting to write master public key " + "to disk"); + tor_free(fname); + goto err; + } + tor_free(fname); + } + } + if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) + sign_signing_key_with_id = NULL; + else + sign_signing_key_with_id = id; + } + + if (master_identity_key && + !ed25519_pubkey_eq(&id->pubkey, &master_identity_key->pubkey)) { + FAIL("Identity key on disk does not match key we loaded earlier!"); + } + + if (need_new_signing_key && NULL == sign_signing_key_with_id) + FAIL("Can't load master key make a new signing key."); + + if (sign_cert) { + if (! sign_cert->signing_key_included) + FAIL("Loaded a signing cert with no key included!"); + if (! ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey)) + FAIL("The signing cert we have was not signed with the master key " + "we loaded!"); + if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0) + FAIL("The signing cert we loaded was not signed correctly!"); + } + + if (want_new_signing_key && sign_signing_key_with_id) { + uint32_t flags = (INIT_ED_KEY_CREATE| + INIT_ED_KEY_REPLACE| + INIT_ED_KEY_EXTRA_STRONG| + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); + char *fname = + options_get_datadir_fname2(options, "keys", "ed25519_signing"); + ed25519_keypair_free(sign); + tor_cert_free(sign_cert); + sign = ed_key_init_from_file(fname, + flags, LOG_WARN, + sign_signing_key_with_id, now, + options->SigningKeyLifetime, + CERT_TYPE_ID_SIGNING, &sign_cert); + tor_free(fname); + if (!sign) + FAIL("Missing signing key"); + use_signing = sign; + + tor_assert(sign_cert->signing_key_included); + tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey)); + tor_assert(ed25519_pubkey_eq(&sign_cert->signed_key, &sign->pubkey)); + } else if (want_new_signing_key) { + static ratelim_t missing_master = RATELIM_INIT(3600); + log_fn_ratelim(&missing_master, LOG_WARN, LD_OR, + "Signing key will expire soon, but I can't load the " + "master key to sign a new one!"); + } + + tor_assert(use_signing); + + /* At this point we no longer need our secret identity key. So wipe + * it, if we loaded it in the first place. */ + memwipe(id->seckey.seckey, 0, sizeof(id->seckey)); + + if (options->command == CMD_KEYGEN) + goto end; + + if (!rsa_ed_crosscert && server_mode(options)) { + uint8_t *crosscert; + ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey, + get_server_identity_key(), + now+10*365*86400,/*XXXX*/ + &crosscert); + rsa_ed_crosscert_len = crosscert_len; + rsa_ed_crosscert = crosscert; + } + + if (!current_auth_key || + EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) { + auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, + now, + options->TestingAuthKeyLifetime, + CERT_TYPE_SIGNING_AUTH, &auth_cert); + + if (!auth) + FAIL("Can't create auth key"); + } + + /* We've generated or loaded everything. Put them in memory. */ + + end: + if (! master_identity_key) { + SET_KEY(master_identity_key, id); + } else { + tor_free(id); + } + if (sign) { + SET_KEY(master_signing_key, sign); + SET_CERT(signing_key_cert, sign_cert); + } + if (auth) { + SET_KEY(current_auth_key, auth); + SET_CERT(auth_key_cert, auth_cert); + } + + return 0; + err: + ed25519_keypair_free(id); + ed25519_keypair_free(sign); + ed25519_keypair_free(auth); + tor_cert_free(sign_cert); + tor_cert_free(auth_cert); + return -1; +} + +/* DOCDOC */ +int +generate_ed_link_cert(const or_options_t *options, time_t now) +{ + const tor_x509_cert_t *link = NULL, *id = NULL; + tor_cert_t *link_cert = NULL; + + if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) { + log_warn(LD_OR, "Can't get my x509 link cert."); + return -1; + } + + const common_digests_t *digests = tor_x509_cert_get_cert_digests(link); + + if (link_cert_cert && + ! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) && + fast_memeq(digests->d[DIGEST_SHA256], link_cert_cert->signed_key.pubkey, + DIGEST256_LEN)) { + return 0; + } + + ed25519_public_key_t dummy_key; + memcpy(dummy_key.pubkey, digests->d[DIGEST_SHA256], DIGEST256_LEN); + + link_cert = tor_cert_create(get_master_signing_keypair(), + CERT_TYPE_SIGNING_LINK, + &dummy_key, + now, + options->TestingLinkCertLifetime, 0); + + if (link_cert) { + SET_CERT(link_cert_cert, link_cert); + } + return 0; +} + +#undef FAIL +#undef SET_KEY +#undef SET_CERT + +int +should_make_new_ed_keys(const or_options_t *options, const time_t now) +{ + if (!master_identity_key || + !master_signing_key || + !current_auth_key || + !link_cert_cert || + EXPIRES_SOON(signing_key_cert, options->TestingSigningKeySlop) || + EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop) || + EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop)) + return 1; + + const tor_x509_cert_t *link = NULL, *id = NULL; + + if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) + return 1; + + const common_digests_t *digests = tor_x509_cert_get_cert_digests(link); + + if (!fast_memeq(digests->d[DIGEST_SHA256], + link_cert_cert->signed_key.pubkey, + DIGEST256_LEN)) { + return 1; + } + + return 0; +} + +#undef EXPIRES_SOON + +const ed25519_public_key_t * +get_master_identity_key(void) +{ + if (!master_identity_key) + return NULL; + return &master_identity_key->pubkey; +} + +const ed25519_keypair_t * +get_master_signing_keypair(void) +{ + return master_signing_key; +} + +const struct tor_cert_st * +get_master_signing_key_cert(void) +{ + return signing_key_cert; +} + +const ed25519_keypair_t * +get_current_auth_keypair(void) +{ + return current_auth_key; +} + +const tor_cert_t * +get_current_link_cert_cert(void) +{ + return link_cert_cert; +} + +const tor_cert_t * +get_current_auth_key_cert(void) +{ + return auth_key_cert; +} + +void +get_master_rsa_crosscert(const uint8_t **cert_out, + size_t *size_out) +{ + *cert_out = rsa_ed_crosscert; + *size_out = rsa_ed_crosscert_len; +} + +/** Construct cross-certification for the master identity key with + * the ntor onion key. Store the sign of the corresponding ed25519 public key + * in *<b>sign_out</b>. */ +tor_cert_t * +make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, time_t now, time_t lifetime, + int *sign_out) +{ + tor_cert_t *cert = NULL; + ed25519_keypair_t ed_onion_key; + + if (ed25519_keypair_from_curve25519_keypair(&ed_onion_key, sign_out, + onion_key) < 0) + goto end; + + cert = tor_cert_create(&ed_onion_key, CERT_TYPE_ONION_ID, master_id_key, + now, lifetime, 0); + + end: + memwipe(&ed_onion_key, 0, sizeof(ed_onion_key)); + return cert; +} + +/** Construct and return an RSA signature for the TAP onion key to + * cross-certify the RSA and Ed25519 identity keys. Set <b>len_out</b> to its + * length. */ +uint8_t * +make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, + const ed25519_public_key_t *master_id_key, + const crypto_pk_t *rsa_id_key, + int *len_out) +{ + uint8_t signature[PK_BYTES]; + uint8_t signed_data[DIGEST_LEN + ED25519_PUBKEY_LEN]; + + *len_out = 0; + crypto_pk_get_digest(rsa_id_key, (char*)signed_data); + memcpy(signed_data + DIGEST_LEN, master_id_key->pubkey, ED25519_PUBKEY_LEN); + + int r = crypto_pk_private_sign(onion_key, + (char*)signature, sizeof(signature), + (const char*)signed_data, sizeof(signed_data)); + if (r < 0) + return NULL; + + *len_out = r; + + return tor_memdup(signature, r); +} + +/** Check whether an RSA-TAP cross-certification is correct. Return 0 if it + * is, -1 if it isn't. */ +int +check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); + int cc_len = + crypto_pk_public_checksig(onion_pkey, + (char*)cc, + crypto_pk_keysize(onion_pkey), + (const char*)crosscert, + crosscert_len); + if (cc_len < 0) { + goto err; + } + if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) { + log_warn(LD_DIR, "Short signature on cross-certification with TAP key"); + goto err; + } + if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) || + tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey, + ED25519_PUBKEY_LEN)) { + log_warn(LD_DIR, "Incorrect cross-certification with TAP key"); + goto err; + } + + tor_free(cc); + return 0; + err: + tor_free(cc); + return -1; +} + +void +routerkeys_free_all(void) +{ + ed25519_keypair_free(master_identity_key); + ed25519_keypair_free(master_signing_key); + ed25519_keypair_free(current_auth_key); + tor_cert_free(signing_key_cert); + tor_cert_free(link_cert_cert); + tor_cert_free(auth_key_cert); + + master_identity_key = master_signing_key = NULL; + current_auth_key = NULL; + signing_key_cert = link_cert_cert = auth_key_cert = NULL; +} + diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h new file mode 100644 index 0000000000..be9b19aea8 --- /dev/null +++ b/src/or/routerkeys.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_ROUTERKEYS_H +#define TOR_ROUTERKEYS_H + +#include "crypto_ed25519.h" + +#define INIT_ED_KEY_CREATE (1u<<0) +#define INIT_ED_KEY_REPLACE (1u<<1) +#define INIT_ED_KEY_SPLIT (1u<<2) +#define INIT_ED_KEY_MISSING_SECRET_OK (1u<<3) +#define INIT_ED_KEY_NEEDCERT (1u<<4) +#define INIT_ED_KEY_EXTRA_STRONG (1u<<5) +#define INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT (1u<<6) +#define INIT_ED_KEY_OMIT_SECRET (1u<<7) +#define INIT_ED_KEY_TRY_ENCRYPTED (1u<<8) +#define INIT_ED_KEY_NO_REPAIR (1u<<9) +#define INIT_ED_KEY_SUGGEST_KEYGEN (1u<<10) +#define INIT_ED_KEY_OFFLINE_SECRET (1u<<11) +#define INIT_ED_KEY_EXPLICIT_FNAME (1u<<12) + +struct tor_cert_st; +ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags, + int severity, + const ed25519_keypair_t *signing_key, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out); +ed25519_keypair_t *ed_key_new(const ed25519_keypair_t *signing_key, + uint32_t flags, + time_t now, + time_t lifetime, + uint8_t cert_type, + struct tor_cert_st **cert_out); +const ed25519_public_key_t *get_master_identity_key(void); +const ed25519_keypair_t *get_master_signing_keypair(void); +const struct tor_cert_st *get_master_signing_key_cert(void); + +const ed25519_keypair_t *get_current_auth_keypair(void); +const struct tor_cert_st *get_current_link_cert_cert(void); +const struct tor_cert_st *get_current_auth_key_cert(void); + +void get_master_rsa_crosscert(const uint8_t **cert_out, + size_t *size_out); + +struct tor_cert_st *make_ntor_onion_key_crosscert( + const curve25519_keypair_t *onion_key, + const ed25519_public_key_t *master_id_key, + time_t now, time_t lifetime, + int *sign_out); +uint8_t *make_tap_onion_key_crosscert(const crypto_pk_t *onion_key, + const ed25519_public_key_t *master_id_key, + const crypto_pk_t *rsa_id_key, + int *len_out); + +int check_tap_onion_key_crosscert(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest); + +int load_ed_keys(const or_options_t *options, time_t now); +int should_make_new_ed_keys(const or_options_t *options, const time_t now); + +int generate_ed_link_cert(const or_options_t *options, time_t now); + +int read_encrypted_secret_key(ed25519_secret_key_t *out, + const char *fname); +int write_encrypted_secret_key(const ed25519_secret_key_t *out, + const char *fname); + +void routerkeys_free_all(void); + +#endif + diff --git a/src/or/routerlist.c b/src/or/routerlist.c index af8e68e880..64baf4d709 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -13,6 +13,8 @@ #define ROUTERLIST_PRIVATE #include "or.h" +#include "backtrace.h" +#include "crypto_ed25519.h" #include "circuitstats.h" #include "config.h" #include "connection.h" @@ -37,7 +39,9 @@ #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "../common/sandbox.h" +#include "sandbox.h" +#include "torcert.h" + // #define DEBUG_ROUTERLIST /****************************************************************************/ @@ -64,8 +68,6 @@ typedef struct cert_list_t cert_list_t; static int compute_weighted_bandwidths(const smartlist_t *sl, bandwidth_weight_rule_t rule, u64_dbl_t **bandwidths_out); -static const routerstatus_t *router_pick_directory_server_impl( - dirinfo_type_t auth, int flags, int *n_busy_out); static const routerstatus_t *router_pick_trusteddirserver_impl( const smartlist_t *sourcelist, dirinfo_type_t auth, int flags, int *n_busy_out); @@ -146,6 +148,22 @@ get_n_authorities(dirinfo_type_t type) return n; } +/** Initialise schedule, want_authority, and increment on in the download + * status dlstatus, then call download_status_reset() on it. + * It is safe to call this function or download_status_reset() multiple times + * on a new dlstatus. But it should *not* be called after a dlstatus has been + * used to count download attempts or failures. */ +static void +download_status_cert_init(download_status_t *dlstatus) +{ + dlstatus->schedule = DL_SCHED_CONSENSUS; + dlstatus->want_authority = DL_WANT_ANY_DIRSERVER; + dlstatus->increment_on = DL_SCHED_INCREMENT_FAILURE; + + /* Use the new schedule to set next_attempt_at */ + download_status_reset(dlstatus); +} + /** Reset the download status of a specified element in a dsmap */ static void download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest) @@ -166,6 +184,7 @@ download_status_reset_by_sk_in_cl(cert_list_t *cl, const char *digest) /* Insert before we reset */ dlstatus = tor_malloc_zero(sizeof(*dlstatus)); dsmap_set(cl->dl_status_map, digest, dlstatus); + download_status_cert_init(dlstatus); } tor_assert(dlstatus); /* Go ahead and reset it */ @@ -204,7 +223,7 @@ download_status_is_ready_by_sk_in_cl(cert_list_t *cl, * too. */ dlstatus = tor_malloc_zero(sizeof(*dlstatus)); - download_status_reset(dlstatus); + download_status_cert_init(dlstatus); dsmap_set(cl->dl_status_map, digest, dlstatus); rv = 1; } @@ -223,7 +242,7 @@ get_cert_list(const char *id_digest) cl = digestmap_get(trusted_dir_certs, id_digest); if (!cl) { cl = tor_malloc_zero(sizeof(cert_list_t)); - cl->dl_status_by_id.schedule = DL_SCHED_CONSENSUS; + download_status_cert_init(&cl->dl_status_by_id); cl->certs = smartlist_new(); cl->dl_status_map = dsmap_new(); digestmap_set(trusted_dir_certs, id_digest, cl); @@ -275,7 +294,7 @@ trusted_dirs_reload_certs(void) /** Helper: return true iff we already have loaded the exact cert * <b>cert</b>. */ -static INLINE int +static inline int already_have_cert(authority_cert_t *cert) { cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest); @@ -660,7 +679,7 @@ static const char *BAD_SIGNING_KEYS[] = { NULL, }; -/** DOCDOC */ +/* DOCDOC */ int authority_cert_is_blacklisted(const authority_cert_t *cert) { @@ -894,8 +913,11 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) if (smartlist_len(fps) > 1) { resource = smartlist_join_strings(fps, "", 0, NULL); + /* We want certs from mirrors, because they will almost always succeed. + */ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0, - resource, PDS_RETRY_IF_NO_SERVERS); + resource, PDS_RETRY_IF_NO_SERVERS, + DL_WANT_ANY_DIRSERVER); tor_free(resource); } /* else we didn't add any: they were all pending */ @@ -938,8 +960,11 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now) if (smartlist_len(fp_pairs) > 1) { resource = smartlist_join_strings(fp_pairs, "", 0, NULL); + /* We want certs from mirrors, because they will almost always succeed. + */ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CERTIFICATE, 0, - resource, PDS_RETRY_IF_NO_SERVERS); + resource, PDS_RETRY_IF_NO_SERVERS, + DL_WANT_ANY_DIRSERVER); tor_free(resource); } /* else they were all pending */ @@ -982,7 +1007,7 @@ router_should_rebuild_store(desc_store_t *store) /** Return the desc_store_t in <b>rl</b> that should be used to store * <b>sd</b>. */ -static INLINE desc_store_t * +static inline desc_store_t * desc_get_store(routerlist_t *rl, const signed_descriptor_t *sd) { if (sd->is_extrainfo) @@ -1292,8 +1317,8 @@ router_get_fallback_dir_servers(void) /** Try to find a running dirserver that supports operations of <b>type</b>. * * If there are no running dirservers in our routerlist and the - * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the authoritative ones - * as running again, and pick one. + * <b>PDS_RETRY_IF_NO_SERVERS</b> flag is set, set all the fallback ones + * (including authorities) as running again, and pick one. * * If the <b>PDS_IGNORE_FASCISTFIREWALL</b> flag is set, then include * dirservers that we can't reach. @@ -1301,8 +1326,9 @@ router_get_fallback_dir_servers(void) * If the <b>PDS_ALLOW_SELF</b> flag is not set, then don't include ourself * (if we're a dirserver). * - * Don't pick an authority if any non-authority is viable; try to avoid using - * servers that have returned 503 recently. + * Don't pick a fallback directory mirror if any non-fallback is viable; + * (the fallback directory mirrors include the authorities) + * try to avoid using servers that have returned 503 recently. */ const routerstatus_t * router_pick_directory_server(dirinfo_type_t type, int flags) @@ -1329,7 +1355,7 @@ router_pick_directory_server(dirinfo_type_t type, int flags) log_info(LD_DIR, "No reachable router entries for dirservers. " "Trying them all again."); - /* mark all authdirservers as up again */ + /* mark all fallback directory mirrors as up again */ mark_all_dirservers_up(fallback_dir_servers); /* try again */ choice = router_pick_directory_server_impl(type, flags, NULL); @@ -1355,15 +1381,21 @@ router_get_trusteddirserver_by_digest(const char *digest) } /** Return the dir_server_t for the fallback dirserver whose identity - * key hashes to <b>digest</b>, or NULL if no such authority is known. + * key hashes to <b>digest</b>, or NULL if no such fallback is in the list of + * fallback_dir_servers. (fallback_dir_servers is affected by the FallbackDir + * and UseDefaultFallbackDirs torrc options.) + * The list of fallback directories includes the list of authorities. */ dir_server_t * router_get_fallback_dirserver_by_digest(const char *digest) { - if (!trusted_dir_servers) + if (!fallback_dir_servers) return NULL; - SMARTLIST_FOREACH(trusted_dir_servers, dir_server_t *, ds, + if (!digest) + return NULL; + + SMARTLIST_FOREACH(fallback_dir_servers, dir_server_t *, ds, { if (tor_memeq(ds->digest, digest, DIGEST_LEN)) return ds; @@ -1372,6 +1404,18 @@ router_get_fallback_dirserver_by_digest(const char *digest) return NULL; } +/** Return 1 if any fallback dirserver's identity key hashes to <b>digest</b>, + * or 0 if no such fallback is in the list of fallback_dir_servers. + * (fallback_dir_servers is affected by the FallbackDir and + * UseDefaultFallbackDirs torrc options.) + * The list of fallback directories includes the list of authorities. + */ +int +router_digest_is_fallback_dir(const char *digest) +{ + return (router_get_fallback_dirserver_by_digest(digest) != NULL); +} + /** Return the dir_server_t for the directory authority whose * v3 identity key hashes to <b>digest</b>, or NULL if no such authority * is known. @@ -1438,9 +1482,190 @@ router_pick_dirserver_generic(smartlist_t *sourcelist, return router_pick_trusteddirserver_impl(sourcelist, type, flags, NULL); } +/* Check if we already have a directory fetch from ap, for serverdesc + * (including extrainfo) or microdesc documents. + * If so, return 1, if not, return 0. + * Also returns 0 if addr is NULL, tor_addr_is_null(addr), or dir_port is 0. + */ +STATIC int +router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc, + int microdesc) +{ + if (!ap || tor_addr_is_null(&ap->addr) || !ap->port) { + return 0; + } + + /* XX/teor - we're not checking tunnel connections here, see #17848 + */ + if (serverdesc && ( + connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_SERVERDESC) + || connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_EXTRAINFO))) { + return 1; + } + + if (microdesc && ( + connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, &ap->addr, ap->port, DIR_PURPOSE_FETCH_MICRODESC))) { + return 1; + } + + return 0; +} + +/* Check if we already have a directory fetch from ds, for serverdesc + * (including extrainfo) or microdesc documents. + * If so, return 1, if not, return 0. + */ +static int +router_is_already_dir_fetching_ds(const dir_server_t *ds, + int serverdesc, + int microdesc) +{ + tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap; + + /* Assume IPv6 DirPort is the same as IPv4 DirPort */ + tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ds->addr); + ipv4_dir_ap.port = ds->dir_port; + tor_addr_copy(&ipv6_dir_ap.addr, &ds->ipv6_addr); + ipv6_dir_ap.port = ds->dir_port; + + return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc) + || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc)); +} + +/* Check if we already have a directory fetch from rs, for serverdesc + * (including extrainfo) or microdesc documents. + * If so, return 1, if not, return 0. + */ +static int +router_is_already_dir_fetching_rs(const routerstatus_t *rs, + int serverdesc, + int microdesc) +{ + tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap; + + /* Assume IPv6 DirPort is the same as IPv4 DirPort */ + tor_addr_from_ipv4h(&ipv4_dir_ap.addr, rs->addr); + ipv4_dir_ap.port = rs->dir_port; + tor_addr_copy(&ipv6_dir_ap.addr, &rs->ipv6_addr); + ipv6_dir_ap.port = rs->dir_port; + + return (router_is_already_dir_fetching(&ipv4_dir_ap, serverdesc, microdesc) + || router_is_already_dir_fetching(&ipv6_dir_ap, serverdesc, microdesc)); +} + +#ifndef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP +#define LOG_FALSE_POSITIVES_DURING_BOOTSTRAP 0 +#endif + +/* Log a message if rs is not found or not a preferred address */ +static void +router_picked_poor_directory_log(const routerstatus_t *rs) +{ + const networkstatus_t *usable_consensus; + usable_consensus = networkstatus_get_reasonably_live_consensus(time(NULL), + usable_consensus_flavor()); + +#if !LOG_FALSE_POSITIVES_DURING_BOOTSTRAP + /* Don't log early in the bootstrap process, it's normal to pick from a + * small pool of nodes. Of course, this won't help if we're trying to + * diagnose bootstrap issues. */ + if (!smartlist_len(nodelist_get_list()) || !usable_consensus + || !router_have_minimum_dir_info()) { + return; + } +#endif + + /* We couldn't find a node, or the one we have doesn't fit our preferences. + * Sometimes this is normal, sometimes it can be a reachability issue. */ + if (!rs) { + /* This happens a lot, so it's at debug level */ + log_debug(LD_DIR, "Wanted to make an outgoing directory connection, but " + "we couldn't find a directory that fit our criteria. " + "Perhaps we will succeed next time with less strict criteria."); + } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1) + && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1) + ) { + /* This is rare, and might be interesting to users trying to diagnose + * connection issues on dual-stack machines. */ + log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir " + "addresses for launching an outgoing connection: " + "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d", + routerstatus_describe(rs), + fmt_addr32(rs->addr), rs->or_port, + rs->dir_port, fmt_addr(&rs->ipv6_addr), + rs->ipv6_orport, rs->dir_port); + } +} + +#undef LOG_FALSE_POSITIVES_DURING_BOOTSTRAP + /** How long do we avoid using a directory server after it's given us a 503? */ #define DIR_503_TIMEOUT (60*60) +/* Common retry code for router_pick_directory_server_impl and + * router_pick_trusteddirserver_impl. Retry with the non-preferred IP version. + * Must be called before RETRY_WITHOUT_EXCLUDE(). + * + * If we got no result, and we are applying IP preferences, and we are a + * client that could use an alternate IP version, try again with the + * opposite preferences. */ +#define RETRY_ALTERNATE_IP_VERSION(retry_label) \ + STMT_BEGIN \ + if (result == NULL && try_ip_pref && options->ClientUseIPv4 \ + && fascist_firewall_use_ipv6(options) && !server_mode(options) \ + && !n_busy) { \ + n_excluded = 0; \ + n_busy = 0; \ + try_ip_pref = 0; \ + goto retry_label; \ + } \ + STMT_END \ + +/* Common retry code for router_pick_directory_server_impl and + * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with + * the preferred IP version. Must be called after RETRY_ALTERNATE_IP_VERSION(). + * + * If we got no result, and we are excluding nodes, and StrictNodes is + * not set, try again without excluding nodes. */ +#define RETRY_WITHOUT_EXCLUDE(retry_label) \ + STMT_BEGIN \ + if (result == NULL && try_excluding && !options->StrictNodes \ + && n_excluded && !n_busy) { \ + try_excluding = 0; \ + n_excluded = 0; \ + n_busy = 0; \ + try_ip_pref = 1; \ + goto retry_label; \ + } \ + STMT_END + +/* When iterating through the routerlist, can OR address/port preference + * and reachability checks be skipped? + */ +static int +router_skip_or_reachability(const or_options_t *options, int try_ip_pref) +{ + /* Servers always have and prefer IPv4. + * And if clients are checking against the firewall for reachability only, + * but there's no firewall, don't bother checking */ + return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or()); +} + +/* When iterating through the routerlist, can Dir address/port preference + * and reachability checks be skipped? + */ +static int +router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) +{ + /* Servers always have and prefer IPv4. + * And if clients are checking against the firewall for reachability only, + * but there's no firewall, don't bother checking */ + return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_dir()); +} + /** Pick a random running valid directory server/mirror from our * routerlist. Arguments are as for router_pick_directory_server(), except: * @@ -1448,7 +1673,7 @@ router_pick_dirserver_generic(smartlist_t *sourcelist, * directories that we excluded for no other reason than * PDS_NO_EXISTING_SERVERDESC_FETCH or PDS_NO_EXISTING_MICRODESC_FETCH. */ -static const routerstatus_t * +STATIC const routerstatus_t * router_pick_directory_server_impl(dirinfo_type_t type, int flags, int *n_busy_out) { @@ -1465,11 +1690,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, const int no_microdesc_fetching = (flags & PDS_NO_EXISTING_MICRODESC_FETCH); const int for_guard = (flags & PDS_FOR_GUARD); int try_excluding = 1, n_excluded = 0, n_busy = 0; + int try_ip_pref = 1; if (!consensus) return NULL; - retry_without_exclude: + retry_search: direct = smartlist_new(); tunnel = smartlist_new(); @@ -1478,17 +1704,20 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); + const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); + const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int must_have_or = directory_must_use_begindir(options); + /* Find all the running dirservers we know about. */ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { int is_trusted, is_trusted_extrainfo; int is_overloaded; - tor_addr_t addr; const routerstatus_t *status = node->rs; const country_t country = node->country; if (!status) continue; - if (!node->is_running || !status->dir_port || !node->is_valid) + if (!node->is_running || !node_is_dir(node) || !node->is_valid) continue; if (requireother && router_digest_is_me(node->identity)) continue; @@ -1498,9 +1727,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, if ((type & EXTRAINFO_DIRINFO) && !router_supports_extrainfo(node->identity, is_trusted_extrainfo)) continue; - if ((type & MICRODESC_DIRINFO) && !is_trusted && - !node->rs->version_supports_microdesc_cache) - continue; /* Don't make the same node a guard twice */ if (for_guard && node->using_as_guard) { continue; @@ -1516,34 +1742,30 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, continue; } - /* XXXX IP6 proposal 118 */ - tor_addr_from_ipv4h(&addr, status->addr); - - if (no_serverdesc_fetching && ( - connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_SERVERDESC) - || connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO) - )) { - ++n_busy; - continue; - } - - if (no_microdesc_fetching && connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, status->dir_port, DIR_PURPOSE_FETCH_MICRODESC) - ) { + if (router_is_already_dir_fetching_rs(status, + no_serverdesc_fetching, + no_microdesc_fetching)) { ++n_busy; continue; } is_overloaded = status->last_dir_503_at + DIR_503_TIMEOUT > now; - if ((!fascistfirewall || - fascist_firewall_allows_address_or(&addr, status->or_port))) + /* Clients use IPv6 addresses if the server has one and the client + * prefers IPv6. + * Add the router if its preferred address and port are reachable. + * If we don't get any routers, we'll try again with the non-preferred + * address for each router (if any). (To ensure correct load-balancing + * we try routers that only have one address both times.) + */ + if (!fascistfirewall || skip_or_fw || + fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, + try_ip_pref)) smartlist_add(is_trusted ? trusted_tunnel : is_overloaded ? overloaded_tunnel : tunnel, (void*)node); - else if (!fascistfirewall || - fascist_firewall_allows_address_dir(&addr, status->dir_port)) + else if (!must_have_or && (skip_dir_fw || + fascist_firewall_allows_node(node, FIREWALL_DIR_CONNECTION, + try_ip_pref))) smartlist_add(is_trusted ? trusted_direct : is_overloaded ? overloaded_direct : direct, (void*)node); } SMARTLIST_FOREACH_END(node); @@ -1574,19 +1796,15 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, smartlist_free(overloaded_direct); smartlist_free(overloaded_tunnel); - if (result == NULL && try_excluding && !options->StrictNodes && n_excluded - && !n_busy) { - /* If we got no result, and we are excluding nodes, and StrictNodes is - * not set, try again without excluding nodes. */ - try_excluding = 0; - n_excluded = 0; - n_busy = 0; - goto retry_without_exclude; - } + RETRY_ALTERNATE_IP_VERSION(retry_search); + + RETRY_WITHOUT_EXCLUDE(retry_search); if (n_busy_out) *n_busy_out = n_busy; + router_picked_poor_directory_log(result ? result->rs : NULL); + return result ? result->rs : NULL; } @@ -1637,30 +1855,36 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, smartlist_t *pick_from; int n_busy = 0; int try_excluding = 1, n_excluded = 0; + int try_ip_pref = 1; if (!sourcelist) return NULL; - retry_without_exclude: + retry_search: direct = smartlist_new(); tunnel = smartlist_new(); overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); + const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); + const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int must_have_or = directory_must_use_begindir(options); + SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d) { int is_overloaded = d->fake_status.last_dir_503_at + DIR_503_TIMEOUT > now; - tor_addr_t addr; if (!d->is_running) continue; if ((type & d->type) == 0) continue; + int is_trusted_extrainfo = router_digest_is_trusted_dir_type( + d->digest, EXTRAINFO_DIRINFO); if ((type & EXTRAINFO_DIRINFO) && - !router_supports_extrainfo(d->digest, 1)) + !router_supports_extrainfo(d->digest, is_trusted_extrainfo)) continue; if (requireother && me && router_digest_is_me(d->digest)) - continue; + continue; if (try_excluding && routerset_contains_routerstatus(options->ExcludeNodes, &d->fake_status, -1)) { @@ -1668,34 +1892,26 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, continue; } - /* XXXX IP6 proposal 118 */ - tor_addr_from_ipv4h(&addr, d->addr); - - if (no_serverdesc_fetching) { - if (connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_SERVERDESC) - || connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_EXTRAINFO)) { - //log_debug(LD_DIR, "We have an existing connection to fetch " - // "descriptor from %s; delaying",d->description); - ++n_busy; - continue; - } - } - if (no_microdesc_fetching) { - if (connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, d->dir_port, DIR_PURPOSE_FETCH_MICRODESC)) { - ++n_busy; - continue; - } + if (router_is_already_dir_fetching_ds(d, no_serverdesc_fetching, + no_microdesc_fetching)) { + ++n_busy; + continue; } - if (d->or_port && - (!fascistfirewall || - fascist_firewall_allows_address_or(&addr, d->or_port))) + /* Clients use IPv6 addresses if the server has one and the client + * prefers IPv6. + * Add the router if its preferred address and port are reachable. + * If we don't get any routers, we'll try again with the non-preferred + * address for each router (if any). (To ensure correct load-balancing + * we try routers that only have one address both times.) + */ + if (!fascistfirewall || skip_or_fw || + fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION, + try_ip_pref)) smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d); - else if (!fascistfirewall || - fascist_firewall_allows_address_dir(&addr, d->dir_port)) + else if (!must_have_or && (skip_dir_fw || + fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION, + try_ip_pref))) smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d); } SMARTLIST_FOREACH_END(d); @@ -1718,22 +1934,19 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, result = &selection->fake_status; } - if (n_busy_out) - *n_busy_out = n_busy; - smartlist_free(direct); smartlist_free(tunnel); smartlist_free(overloaded_direct); smartlist_free(overloaded_tunnel); - if (result == NULL && try_excluding && !options->StrictNodes && n_excluded) { - /* If we got no result, and we are excluding nodes, and StrictNodes is - * not set, try again without excluding nodes. */ - try_excluding = 0; - n_excluded = 0; - goto retry_without_exclude; - } + RETRY_ALTERNATE_IP_VERSION(retry_search); + RETRY_WITHOUT_EXCLUDE(retry_search); + + router_picked_poor_directory_log(result); + + if (n_busy_out) + *n_busy_out = n_busy; return result; } @@ -1803,8 +2016,12 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router) void router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid, int need_uptime, int need_capacity, - int need_guard, int need_desc) -{ /* XXXX MOVE */ + int need_guard, int need_desc, + int pref_addr, int direct_conn) +{ + const int check_reach = !router_skip_or_reachability(get_options(), + pref_addr); + /* XXXX MOVE */ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { if (!node->is_running || (!node->is_valid && !allow_invalid)) @@ -1815,6 +2032,11 @@ router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid, continue; if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) continue; + /* Choose a node with an OR address that matches the firewall rules, + * if we are making a direct connection */ + if (direct_conn && check_reach && + !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr)) + continue; smartlist_add(sl, (void *)node); } SMARTLIST_FOREACH_END(node); @@ -1897,7 +2119,7 @@ scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries, #if SIZEOF_VOID_P == 8 #define gt_i64_timei(a,b) ((a) > (b)) #else -static INLINE int +static inline int gt_i64_timei(uint64_t a, uint64_t b) { int64_t diff = (int64_t) (b - a); @@ -1975,7 +2197,7 @@ bridge_get_advertised_bandwidth_bounded(routerinfo_t *router) /** Return bw*1000, unless bw*1000 would overflow, in which case return * INT32_MAX. */ -static INLINE int32_t +static inline int32_t kb_to_bytes(uint32_t bw) { return (bw > (INT32_MAX/1000)) ? INT32_MAX : bw*1000; @@ -2276,6 +2498,10 @@ node_sl_choose_by_bandwidth(const smartlist_t *sl, * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that * have a routerinfo or microdescriptor -- that is, enough info to be * used to build a circuit. + * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that + * have an address that is preferred by the ClientPreferIPv6ORPort setting + * (regardless of this flag, we exclude nodes that aren't allowed by the + * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0). */ const node_t * router_choose_random_node(smartlist_t *excludedsmartlist, @@ -2288,6 +2514,8 @@ router_choose_random_node(smartlist_t *excludedsmartlist, const int allow_invalid = (flags & CRN_ALLOW_INVALID) != 0; const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0; const int need_desc = (flags & CRN_NEED_DESC) != 0; + const int pref_addr = (flags & CRN_PREF_ADDR) != 0; + const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; smartlist_t *sl=smartlist_new(), *excludednodes=smartlist_new(); @@ -2313,7 +2541,8 @@ router_choose_random_node(smartlist_t *excludedsmartlist, router_add_running_nodes_to_smartlist(sl, allow_invalid, need_uptime, need_capacity, - need_guard, need_desc); + need_guard, need_desc, pref_addr, + direct_conn); log_debug(LD_CIRC, "We found %d running nodes.", smartlist_len(sl)); @@ -2342,7 +2571,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist, choice = node_sl_choose_by_bandwidth(sl, rule); smartlist_free(sl); - if (!choice && (need_uptime || need_capacity || need_guard)) { + if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) { /* try once more -- recurse but with fewer restrictions. */ log_info(LD_CIRC, "We couldn't find any live%s%s%s routers; falling back " @@ -2350,7 +2579,8 @@ router_choose_random_node(smartlist_t *excludedsmartlist, need_capacity?", fast":"", need_uptime?", stable":"", need_guard?", guard":""); - flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD); + flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD| + CRN_PREF_ADDR); choice = router_choose_random_node( excludedsmartlist, excludedset, flags); } @@ -2669,6 +2899,7 @@ routerinfo_free(routerinfo_t *router) tor_free(router->onion_curve25519_pkey); if (router->identity_pkey) crypto_pk_free(router->identity_pkey); + tor_cert_free(router->cache_info.signing_key_cert); if (router->declared_family) { SMARTLIST_FOREACH(router->declared_family, char *, s, tor_free(s)); smartlist_free(router->declared_family); @@ -2687,6 +2918,7 @@ extrainfo_free(extrainfo_t *extrainfo) { if (!extrainfo) return; + tor_cert_free(extrainfo->cache_info.signing_key_cert); tor_free(extrainfo->cache_info.signed_descriptor_body); tor_free(extrainfo->pending_sig); @@ -2702,11 +2934,25 @@ signed_descriptor_free(signed_descriptor_t *sd) return; tor_free(sd->signed_descriptor_body); + tor_cert_free(sd->signing_key_cert); memset(sd, 99, sizeof(signed_descriptor_t)); /* Debug bad mem usage */ tor_free(sd); } +/** Copy src into dest, and steal all references inside src so that when + * we free src, we don't mess up dest. */ +static void +signed_descriptor_move(signed_descriptor_t *dest, + signed_descriptor_t *src) +{ + tor_assert(dest != src); + memcpy(dest, src, sizeof(signed_descriptor_t)); + src->signed_descriptor_body = NULL; + src->signing_key_cert = NULL; + dest->routerlist_index = -1; +} + /** Extract a signed_descriptor_t from a general routerinfo, and free the * routerinfo. */ @@ -2716,9 +2962,7 @@ signed_descriptor_from_routerinfo(routerinfo_t *ri) signed_descriptor_t *sd; tor_assert(ri->purpose == ROUTER_PURPOSE_GENERAL); sd = tor_malloc_zero(sizeof(signed_descriptor_t)); - memcpy(sd, &(ri->cache_info), sizeof(signed_descriptor_t)); - sd->routerlist_index = -1; - ri->cache_info.signed_descriptor_body = NULL; + signed_descriptor_move(sd, &ri->cache_info); routerinfo_free(ri); return sd; } @@ -2788,7 +3032,7 @@ dump_routerlist_mem_usage(int severity) * in <b>sl</b> at position <b>idx</b>. Otherwise, search <b>sl</b> for * <b>ri</b>. Return the index of <b>ri</b> in <b>sl</b>, or -1 if <b>ri</b> * is not in <b>sl</b>. */ -static INLINE int +static inline int routerlist_find_elt_(smartlist_t *sl, void *ri, int idx) { if (idx < 0) { @@ -2896,7 +3140,7 @@ extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei, int warn_if_incompatible)) "Mismatch in digest in extrainfo map."); goto done; } - if (routerinfo_incompatible_with_extrainfo(ri, ei, sd, + if (routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, sd, &compatibility_error_msg)) { char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1]; r = (ri->cache_info.extrainfo_is_bogus) ? @@ -3204,16 +3448,18 @@ routerlist_reparse_old(routerlist_t *rl, signed_descriptor_t *sd) 0, 1, NULL, NULL); if (!ri) return NULL; - memcpy(&ri->cache_info, sd, sizeof(signed_descriptor_t)); - sd->signed_descriptor_body = NULL; /* Steal reference. */ - ri->cache_info.routerlist_index = -1; + signed_descriptor_move(&ri->cache_info, sd); routerlist_remove_old(rl, sd, -1); return ri; } -/** Free all memory held by the routerlist module. */ +/** Free all memory held by the routerlist module. + * Note: Calling routerlist_free_all() should always be paired with + * a call to nodelist_free_all(). These should only be called during + * cleanup. + */ void routerlist_free_all(void) { @@ -3297,6 +3543,13 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, old_router = router_get_mutable_by_digest(id_digest); + /* Make sure that it isn't expired. */ + if (router->cert_expiration_time < approx_time()) { + routerinfo_free(router); + *msg = "Some certs on this router are expired."; + return ROUTER_CERTS_EXPIRED; + } + /* Make sure that we haven't already got this exact descriptor. */ if (sdmap_get(routerlist->desc_digest_map, router->cache_info.signed_descriptor_digest)) { @@ -4010,12 +4263,10 @@ update_all_descriptor_downloads(time_t now) void routerlist_retry_directory_downloads(time_t now) { + (void)now; router_reset_status_download_failures(); router_reset_descriptor_download_failures(); - if (get_options()->DisableNetwork) - return; - update_networkstatus_downloads(now); - update_all_descriptor_downloads(now); + reschedule_directory_downloads(); } /** Return true iff <b>router</b> does not permit exit streams. @@ -4027,15 +4278,16 @@ router_exit_policy_rejects_all(const routerinfo_t *router) } /** Create an directory server at <b>address</b>:<b>port</b>, with OR identity - * key <b>digest</b>. If <b>address</b> is NULL, add ourself. If - * <b>is_authority</b>, this is a directory authority. Return the new - * directory server entry on success or NULL on failure. */ + * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL, + * add ourself. If <b>is_authority</b>, this is a directory authority. Return + * the new directory server entry on success or NULL on failure. */ static dir_server_t * dir_server_new(int is_authority, const char *nickname, const tor_addr_t *addr, const char *hostname, uint16_t dir_port, uint16_t or_port, + const tor_addr_port_t *addrport_ipv6, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) @@ -4044,13 +4296,15 @@ dir_server_new(int is_authority, uint32_t a; char *hostname_ = NULL; + tor_assert(digest); + if (weight < 0) return NULL; if (tor_addr_family(addr) == AF_INET) a = tor_addr_to_ipv4h(addr); else - return NULL; /*XXXX Support IPv6 */ + return NULL; if (!hostname) hostname_ = tor_dup_addr(addr); @@ -4067,18 +4321,31 @@ dir_server_new(int is_authority, ent->is_authority = is_authority; ent->type = type; ent->weight = weight; + if (addrport_ipv6) { + if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) { + log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6."); + tor_addr_make_unspec(&ent->ipv6_addr); + } else { + tor_addr_copy(&ent->ipv6_addr, &addrport_ipv6->addr); + ent->ipv6_orport = addrport_ipv6->port; + } + } else { + tor_addr_make_unspec(&ent->ipv6_addr); + } + memcpy(ent->digest, digest, DIGEST_LEN); if (v3_auth_digest && (type & V3_DIRINFO)) memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN); if (nickname) tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d", - nickname, hostname, (int)dir_port); + nickname, hostname_, (int)dir_port); else tor_asprintf(&ent->description, "directory server at %s:%d", - hostname, (int)dir_port); + hostname_, (int)dir_port); ent->fake_status.addr = ent->addr; + tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr); memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN); if (nickname) strlcpy(ent->fake_status.nickname, nickname, @@ -4087,6 +4354,7 @@ dir_server_new(int is_authority, ent->fake_status.nickname[0] = '\0'; ent->fake_status.dir_port = ent->dir_port; ent->fake_status.or_port = ent->or_port; + ent->fake_status.ipv6_orport = ent->ipv6_orport; return ent; } @@ -4098,6 +4366,7 @@ dir_server_new(int is_authority, dir_server_t * trusted_dir_server_new(const char *nickname, const char *address, uint16_t dir_port, uint16_t or_port, + const tor_addr_port_t *ipv6_addrport, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { @@ -4128,7 +4397,9 @@ trusted_dir_server_new(const char *nickname, const char *address, tor_addr_from_ipv4h(&addr, a); result = dir_server_new(1, nickname, &addr, hostname, - dir_port, or_port, digest, + dir_port, or_port, + ipv6_addrport, + digest, v3_auth_digest, type, weight); tor_free(hostname); return result; @@ -4140,9 +4411,12 @@ trusted_dir_server_new(const char *nickname, const char *address, dir_server_t * fallback_dir_server_new(const tor_addr_t *addr, uint16_t dir_port, uint16_t or_port, + const tor_addr_port_t *addrport_ipv6, const char *id_digest, double weight) { - return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, id_digest, + return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, + addrport_ipv6, + id_digest, NULL, ALL_DIRINFO, weight); } @@ -4211,7 +4485,7 @@ clear_dir_servers(void) /** For every current directory connection whose purpose is <b>purpose</b>, * and where the resource being downloaded begins with <b>prefix</b>, split * rest of the resource into base16 fingerprints (or base64 fingerprints if - * purpose==DIR_PURPPOSE_FETCH_MICRODESC), decode them, and set the + * purpose==DIR_PURPOSE_FETCH_MICRODESC), decode them, and set the * corresponding elements of <b>result</b> to a nonzero value. */ static void @@ -4367,14 +4641,14 @@ MOCK_IMPL(STATIC void, initiate_descriptor_downloads, tor_free(cp); if (source) { - /* We know which authority we want. */ + /* We know which authority or directory mirror we want. */ directory_initiate_command_routerstatus(source, purpose, ROUTER_PURPOSE_GENERAL, DIRIND_ONEHOP, resource, NULL, 0, 0); } else { directory_get_from_dirserver(purpose, ROUTER_PURPOSE_GENERAL, resource, - pds_flags); + pds_flags, DL_WANT_ANY_DIRSERVER); } tor_free(resource); } @@ -4385,17 +4659,24 @@ static int max_dl_per_request(const or_options_t *options, int purpose) { /* Since squid does not like URLs >= 4096 bytes we limit it to 96. - * 4096 - strlen(http://255.255.255.255/tor/server/d/.z) == 4058 - * 4058/41 (40 for the hash and 1 for the + that separates them) => 98 + * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 + * /tor/server/d/.z) == 4026 + * 4026/41 (40 for the hash and 1 for the + that separates them) => 98 * So use 96 because it's a nice number. + * + * For microdescriptors, the calculation is + * 4096 - strlen(http://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535 + * /tor/micro/d/.z) == 4027 + * 4027/44 (43 for the hash and 1 for the - that separates them) => 91 + * So use 90 because it's a nice number. */ int max = 96; if (purpose == DIR_PURPOSE_FETCH_MICRODESC) { - max = 92; + max = 90; } /* If we're going to tunnel our connections, we can ask for a lot more * in a request. */ - if (!directory_fetches_from_authorities(options)) { + if (directory_must_use_begindir(options)) { max = 500; } return max; @@ -4656,9 +4937,14 @@ launch_dummy_descriptor_download_as_needed(time_t now, last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now && last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) { last_dummy_download = now; + /* XX/teor - do we want an authority here, because they are less likely + * to give us the wrong address? (See #17782) + * I'm leaving the previous behaviour intact, because I don't like + * the idea of some relays contacting an authority every 20 minutes. */ directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, ROUTER_PURPOSE_GENERAL, "authority.z", - PDS_RETRY_IF_NO_SERVERS); + PDS_RETRY_IF_NO_SERVERS, + DL_WANT_ANY_DIRSERVER); } } @@ -4844,7 +5130,9 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) (r1->contact_info && r2->contact_info && strcasecmp(r1->contact_info, r2->contact_info)) || r1->is_hibernating != r2->is_hibernating || - cmp_addr_policies(r1->exit_policy, r2->exit_policy)) + cmp_addr_policies(r1->exit_policy, r2->exit_policy) || + (r1->supports_tunnelled_dir_requests != + r2->supports_tunnelled_dir_requests)) return 0; if ((r1->declared_family == NULL) != (r2->declared_family == NULL)) return 0; @@ -4889,25 +5177,32 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) return 1; } -/** Check whether <b>ri</b> (a.k.a. sd) is a router compatible with the - * extrainfo document - * <b>ei</b>. If no router is compatible with <b>ei</b>, <b>ei</b> should be +/** Check whether <b>sd</b> describes a router descriptor compatible with the + * extrainfo document <b>ei</b>. + * + * <b>identity_pkey</b> (which must also be provided) is RSA1024 identity key + * for the router. We use it to check the signature of the extrainfo document, + * if it has not already been checked. + * + * If no router is compatible with <b>ei</b>, <b>ei</b> should be * dropped. Return 0 for "compatible", return 1 for "reject, and inform * whoever uploaded <b>ei</b>, and return -1 for "reject silently.". If * <b>msg</b> is present, set *<b>msg</b> to a description of the * incompatibility (if any). + * + * Set the extrainfo_is_bogus field in <b>sd</b> if the digests matched + * but the extrainfo was nonetheless incompatible. **/ int -routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, +routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey, extrainfo_t *ei, signed_descriptor_t *sd, const char **msg) { - int digest_matches, r=1; - tor_assert(ri); + int digest_matches, digest256_matches, r=1; + tor_assert(identity_pkey); + tor_assert(sd); tor_assert(ei); - if (!sd) - sd = (signed_descriptor_t*)&ri->cache_info; if (ei->bad_sig) { if (msg) *msg = "Extrainfo signature was bad, or signed with wrong key."; @@ -4916,19 +5211,31 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, digest_matches = tor_memeq(ei->cache_info.signed_descriptor_digest, sd->extra_info_digest, DIGEST_LEN); + /* Set digest256_matches to 1 if the digest is correct, or if no + * digest256 was in the ri. */ + digest256_matches = tor_memeq(ei->digest256, + sd->extra_info_digest256, DIGEST256_LEN); + digest256_matches |= + tor_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN); /* The identity must match exactly to have been generated at the same time * by the same router. */ - if (tor_memneq(ri->cache_info.identity_digest, + if (tor_memneq(sd->identity_digest, ei->cache_info.identity_digest, DIGEST_LEN)) { if (msg) *msg = "Extrainfo nickname or identity did not match routerinfo"; goto err; /* different servers */ } + if (! tor_cert_opt_eq(sd->signing_key_cert, + ei->cache_info.signing_key_cert)) { + if (msg) *msg = "Extrainfo signing key cert didn't match routerinfo"; + goto err; /* different servers */ + } + if (ei->pending_sig) { char signed_digest[128]; - if (crypto_pk_public_checksig(ri->identity_pkey, + if (crypto_pk_public_checksig(identity_pkey, signed_digest, sizeof(signed_digest), ei->pending_sig, ei->pending_sig_len) != DIGEST_LEN || tor_memneq(signed_digest, ei->cache_info.signed_descriptor_digest, @@ -4939,7 +5246,7 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, goto err; /* Bad signature, or no match. */ } - ei->cache_info.send_unencrypted = ri->cache_info.send_unencrypted; + ei->cache_info.send_unencrypted = sd->send_unencrypted; tor_free(ei->pending_sig); } @@ -4952,6 +5259,17 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, goto err; } + if (!digest256_matches && !digest_matches) { + if (msg) *msg = "Neither digest256 or digest matched " + "digest from routerdesc"; + goto err; + } + + if (!digest256_matches) { + if (msg) *msg = "Extrainfo digest did not match digest256 from routerdesc"; + goto err; /* Digest doesn't match declared value. */ + } + if (!digest_matches) { if (msg) *msg = "Extrainfo digest did not match value from routerdesc"; goto err; /* Digest doesn't match declared value. */ @@ -5119,81 +5437,3 @@ refresh_all_country_info(void) nodelist_refresh_countries(); } -/** Determine the routers that are responsible for <b>id</b> (binary) and - * add pointers to those routers' routerstatus_t to <b>responsible_dirs</b>. - * Return -1 if we're returning an empty smartlist, else return 0. - */ -int -hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, - const char *id) -{ - int start, found, n_added = 0, i; - networkstatus_t *c = networkstatus_get_latest_consensus(); - if (!c || !smartlist_len(c->routerstatus_list)) { - log_warn(LD_REND, "We don't have a consensus, so we can't perform v2 " - "rendezvous operations."); - return -1; - } - tor_assert(id); - start = networkstatus_vote_find_entry_idx(c, id, &found); - if (start == smartlist_len(c->routerstatus_list)) start = 0; - i = start; - do { - routerstatus_t *r = smartlist_get(c->routerstatus_list, i); - if (r->is_hs_dir) { - smartlist_add(responsible_dirs, r); - if (++n_added == REND_NUMBER_OF_CONSECUTIVE_REPLICAS) - return 0; - } - if (++i == smartlist_len(c->routerstatus_list)) - i = 0; - } while (i != start); - - /* Even though we don't have the desired number of hidden service - * directories, be happy if we got any. */ - return smartlist_len(responsible_dirs) ? 0 : -1; -} - -/** Return true if this node is currently acting as hidden service - * directory, false otherwise. */ -int -hid_serv_acting_as_directory(void) -{ - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return 0; - if (!get_options()->HidServDirectoryV2) { - log_info(LD_REND, "We are not acting as hidden service directory, " - "because we have not been configured as such."); - return 0; - } - return 1; -} - -/** Return true if this node is responsible for storing the descriptor ID - * in <b>query</b> and false otherwise. */ -int -hid_serv_responsible_for_desc_id(const char *query) -{ - const routerinfo_t *me; - routerstatus_t *last_rs; - const char *my_id, *last_id; - int result; - smartlist_t *responsible; - if (!hid_serv_acting_as_directory()) - return 0; - if (!(me = router_get_my_routerinfo())) - return 0; /* This is redundant, but let's be paranoid. */ - my_id = me->cache_info.identity_digest; - responsible = smartlist_new(); - if (hid_serv_get_responsible_directories(responsible, query) < 0) { - smartlist_free(responsible); - return 0; - } - last_rs = smartlist_get(responsible, smartlist_len(responsible)-1); - last_id = last_rs->identity_digest; - result = rend_id_is_in_interval(my_id, query, last_id); - smartlist_free(responsible); - return result; -} - diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 78c3fbb880..cb5b42a3b8 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -50,6 +50,7 @@ const routerstatus_t *router_pick_directory_server(dirinfo_type_t type, dir_server_t *router_get_trusteddirserver_by_digest(const char *d); dir_server_t *router_get_fallback_dirserver_by_digest( const char *digest); +int router_digest_is_fallback_dir(const char *digest); dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d); const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type, int flags); @@ -60,7 +61,8 @@ void router_reset_status_download_failures(void); int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2); void router_add_running_nodes_to_smartlist(smartlist_t *sl, int allow_invalid, int need_uptime, int need_capacity, - int need_guard, int need_desc); + int need_guard, int need_desc, + int pref_addr, int direct_conn); const routerinfo_t *routerlist_find_my_routerinfo(void); uint32_t router_get_advertised_bandwidth(const routerinfo_t *router); @@ -109,7 +111,7 @@ static int WRA_NEVER_DOWNLOADABLE(was_router_added_t s); * was added. It might still be necessary to check whether the descriptor * generator should be notified. */ -static INLINE int +static inline int WRA_WAS_ADDED(was_router_added_t s) { return s == ROUTER_ADDED_SUCCESSFULLY || s == ROUTER_ADDED_NOTIFY_GENERATOR; } @@ -118,27 +120,30 @@ WRA_WAS_ADDED(was_router_added_t s) { * - not in the consensus * - neither in the consensus nor in any networkstatus document * - it was outdated. + * - its certificates were expired. */ -static INLINE int WRA_WAS_OUTDATED(was_router_added_t s) +static inline int WRA_WAS_OUTDATED(was_router_added_t s) { return (s == ROUTER_WAS_TOO_OLD || s == ROUTER_IS_ALREADY_KNOWN || s == ROUTER_NOT_IN_CONSENSUS || - s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS); + s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS || + s == ROUTER_CERTS_EXPIRED); } /** Return true iff the outcome code in <b>s</b> indicates that the descriptor * was flat-out rejected. */ -static INLINE int WRA_WAS_REJECTED(was_router_added_t s) +static inline int WRA_WAS_REJECTED(was_router_added_t s) { return (s == ROUTER_AUTHDIR_REJECTS); } /** Return true iff the outcome code in <b>s</b> indicates that the descriptor * was flat-out rejected. */ -static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s) +static inline int WRA_NEVER_DOWNLOADABLE(was_router_added_t s) { return (s == ROUTER_AUTHDIR_REJECTS || s == ROUTER_BAD_EI || - s == ROUTER_WAS_TOO_OLD); + s == ROUTER_WAS_TOO_OLD || + s == ROUTER_CERTS_EXPIRED); } was_router_added_t router_add_to_routerlist(routerinfo_t *router, const char **msg, @@ -167,10 +172,12 @@ int router_exit_policy_rejects_all(const routerinfo_t *router); dir_server_t *trusted_dir_server_new(const char *nickname, const char *address, uint16_t dir_port, uint16_t or_port, + const tor_addr_port_t *addrport_ipv6, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight); dir_server_t *fallback_dir_server_new(const tor_addr_t *addr, uint16_t dir_port, uint16_t or_port, + const tor_addr_port_t *addrport_ipv6, const char *id_digest, double weight); void dir_server_add(dir_server_t *ent); @@ -184,7 +191,7 @@ void update_extrainfo_downloads(time_t now); void router_reset_descriptor_download_failures(void); int router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2); -int routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri, +int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri, extrainfo_t *ei, signed_descriptor_t *sd, const char **msg); @@ -195,11 +202,6 @@ void routers_sort_by_identity(smartlist_t *routers); void refresh_all_country_info(void); -int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs, - const char *id); -int hid_serv_acting_as_directory(void); -int hid_serv_responsible_for_desc_id(const char *id); - void list_pending_microdesc_downloads(digest256map_t *result); void launch_descriptor_downloads(int purpose, smartlist_t *downloadable, @@ -227,6 +229,9 @@ STATIC int choose_array_element_by_weight(const u64_dbl_t *entries, int n_entries); STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries, uint64_t *total_out); +STATIC const routerstatus_t *router_pick_directory_server_impl( + dirinfo_type_t auth, int flags, + int *n_busy_out); MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router, int seconds)); @@ -236,6 +241,8 @@ MOCK_DECL(STATIC was_router_added_t, extrainfo_insert, MOCK_DECL(STATIC void, initiate_descriptor_downloads, (const routerstatus_t *source, int purpose, smartlist_t *digests, int lo, int hi, int pds_flags)); +STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap, + int serverdesc, int microdesc); #endif diff --git a/src/or/routerparse.c b/src/or/routerparse.c index dcf419798a..91025c1568 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -1,7 +1,7 @@ - /* Copyright (c) 2001 Matej Pfajfar. +/* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -24,16 +24,20 @@ #include "microdesc.h" #include "networkstatus.h" #include "rephist.h" +#include "routerkeys.h" #include "routerparse.h" #include "entrynodes.h" +#include "torcert.h" + #undef log #include <math.h> /****************************************************************************/ /** Enumeration of possible token types. The ones starting with K_ correspond - * to directory 'keywords'. ERR_ is an error in the tokenizing process, EOF_ - * is an end-of-file marker, and NIL_ is used to encode not-a-token. + * to directory 'keywords'. A_ is for an annotation, R or C is related to + * hidden services, ERR_ is an error in the tokenizing process, EOF_ is an + * end-of-file marker, and NIL_ is used to encode not-a-token. */ typedef enum { K_ACCEPT = 0, @@ -69,6 +73,7 @@ typedef enum { K_CLIENT_VERSIONS, K_SERVER_VERSIONS, K_OR_ADDRESS, + K_ID, K_P, K_P6, K_R, @@ -83,6 +88,11 @@ typedef enum { K_HIDDEN_SERVICE_DIR, K_ALLOW_SINGLE_HOP_EXITS, K_IPV6_POLICY, + K_ROUTER_SIG_ED25519, + K_IDENTITY_ED25519, + K_MASTER_KEY_ED25519, + K_ONION_KEY_CROSSCERT, + K_NTOR_ONION_KEY_CROSSCERT, K_DIRREQ_END, K_DIRREQ_V2_IPS, @@ -116,6 +126,7 @@ typedef enum { K_DIR_KEY_CERTIFICATION, K_DIR_KEY_CROSSCERT, K_DIR_ADDRESS, + K_DIR_TUNNELLED, K_VOTE_STATUS, K_VALID_AFTER, @@ -293,6 +304,13 @@ static token_rule_t routerdesc_token_table[] = { T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ), T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ), + T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T01("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ), + T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ), + T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT, + EQ(1), NEED_OBJ ), + T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), @@ -302,6 +320,7 @@ static token_rule_t routerdesc_token_table[] = { T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ), A01("@purpose", A_PURPOSE, GE(1), NO_OBJ ), + T01("tunnelled-dir-server",K_DIR_TUNNELLED, NO_ARGS, NO_OBJ ), END_OF_TABLE }; @@ -310,6 +329,8 @@ static token_rule_t routerdesc_token_table[] = { static token_rule_t extrainfo_token_table[] = { T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ), T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ), + T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ), + T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), @@ -353,6 +374,7 @@ static token_rule_t rtrstatus_token_table[] = { T01("v", K_V, CONCAT_ARGS, NO_OBJ ), T01("w", K_W, ARGS, NO_OBJ ), T0N("m", K_M, CONCAT_ARGS, NO_OBJ ), + T0N("id", K_ID, GE(2), NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), END_OF_TABLE }; @@ -490,6 +512,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = { static token_rule_t microdesc_token_table[] = { T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024), T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ), + T0N("id", K_ID, GE(2), NO_OBJ ), T0N("a", K_A, GE(1), NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), T01("p", K_P, CONCAT_ARGS, NO_OBJ ), @@ -506,12 +529,16 @@ static addr_policy_t *router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags); static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok); +static int router_get_hash_impl_helper(const char *s, size_t s_len, + const char *start_str, + const char *end_str, char end_c, + const char **start_out, const char **end_out); static int router_get_hash_impl(const char *s, size_t s_len, char *digest, const char *start_str, const char *end_str, char end_char, digest_algorithm_t alg); static int router_get_hashes_impl(const char *s, size_t s_len, - digests_t *digests, + common_digests_t *digests, const char *start_str, const char *end_str, char end_char); static void token_clear(directory_token_t *tok); @@ -611,7 +638,7 @@ router_get_router_hash(const char *s, size_t s_len, char *digest) /** Set <b>digests</b> to all the digests of the consensus document in * <b>s</b> */ int -router_get_networkstatus_v3_hashes(const char *s, digests_t *digests) +router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests) { return router_get_hashes_impl(s,strlen(s),digests, "network-status-version", @@ -637,7 +664,7 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest) char * router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key) + const crypto_pk_t *private_key) { char *signature; size_t i, keysize; @@ -664,7 +691,8 @@ router_get_dirobj_signature(const char *digest, goto truncated; i = strlen(buf); - if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) { + if (base64_encode(buf+i, buf_len-i, signature, siglen, + BASE64_ENCODE_MULTILINE) < 0) { log_warn(LD_BUG,"couldn't base64-encode signature"); goto err; } @@ -857,8 +885,8 @@ check_signature_token(const char *digest, tor_free(signed_digest); return -1; } -// log_debug(LD_DIR,"Signed %s hash starts %s", doctype, -// hex_str(signed_digest,4)); + // log_debug(LD_DIR,"Signed %s hash starts %s", doctype, + // hex_str(signed_digest,4)); if (tor_memneq(digest, signed_digest, digest_len)) { log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype); tor_free(signed_digest); @@ -1105,6 +1133,7 @@ router_parse_entry_from_string(const char *s, const char *end, size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0; int ok = 1; memarea_t *area = NULL; + tor_cert_t *ntor_cc_cert = NULL; /* Do not set this to '1' until we have parsed everything that we intend to * parse that's covered by the hash. */ int can_dl_again = 0; @@ -1177,9 +1206,11 @@ router_parse_entry_from_string(const char *s, const char *end, } tok = find_by_keyword(tokens, K_ROUTER); + const int router_token_pos = smartlist_pos(tokens, tok); tor_assert(tok->n_args >= 5); router = tor_malloc_zero(sizeof(routerinfo_t)); + router->cert_expiration_time = TIME_MAX; router->cache_info.routerlist_index = -1; router->cache_info.annotations_len = s-start_of_annotations + prepend_len; router->cache_info.signed_descriptor_len = end-s; @@ -1310,6 +1341,173 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; } + { + directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok, + *master_key_tok; + ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); + ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); + master_key_tok = find_opt_by_keyword(tokens, K_MASTER_KEY_ED25519); + cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT); + cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT); + int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok + + !!cc_tap_tok + !!cc_ntor_tok; + if ((n_ed_toks != 0 && n_ed_toks != 4) || + (n_ed_toks == 4 && !router->onion_curve25519_pkey)) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519/" + "cross-certification support"); + goto err; + } + if (master_key_tok && !ed_sig_tok) { + log_warn(LD_DIR, "Router descriptor has ed25519 master key but no " + "certificate"); + goto err; + } + if (ed_sig_tok) { + tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok); + const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); + if (ed_cert_token_pos == -1 || router_token_pos == -1 || + (ed_cert_token_pos != router_token_pos + 1 && + ed_cert_token_pos != router_token_pos - 1)) { + log_warn(LD_DIR, "Ed25519 certificate in wrong position"); + goto err; + } + if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { + log_warn(LD_DIR, "Ed25519 signature in wrong position"); + goto err; + } + if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + goto err; + } + if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) { + log_warn(LD_DIR, "Wrong object type on onion-key-crosscert " + "in decriptor"); + goto err; + } + if (strcmp(cc_ntor_tok->args[0], "0") && + strcmp(cc_ntor_tok->args[0], "1")) { + log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert"); + goto err; + } + int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1"); + + uint8_t d256[DIGEST256_LEN]; + const char *signed_start, *signed_end; + tor_cert_t *cert = tor_cert_parse( + (const uint8_t*)ed_cert_tok->object_body, + ed_cert_tok->object_size); + if (! cert) { + log_warn(LD_DIR, "Couldn't parse ed25519 cert"); + goto err; + } + /* makes sure it gets freed. */ + router->cache_info.signing_key_cert = cert; + + if (cert->cert_type != CERT_TYPE_ID_SIGNING || + ! cert->signing_key_included) { + log_warn(LD_DIR, "Invalid form for ed25519 cert"); + goto err; + } + + if (master_key_tok) { + /* This token is optional, but if it's present, it must match + * the signature in the signing cert, or supplant it. */ + tor_assert(master_key_tok->n_args >= 1); + ed25519_public_key_t pkey; + if (ed25519_public_from_base64(&pkey, master_key_tok->args[0])<0) { + log_warn(LD_DIR, "Can't parse ed25519 master key"); + goto err; + } + + if (fast_memneq(&cert->signing_key.pubkey, + pkey.pubkey, ED25519_PUBKEY_LEN)) { + log_warn(LD_DIR, "Ed25519 master key does not match " + "key in certificate"); + goto err; + } + } + ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, + cc_ntor_tok->object_size); + if (!ntor_cc_cert) { + log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert"); + goto err; + } + if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID || + ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) { + log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert"); + goto err; + } + + ed25519_public_key_t ntor_cc_pk; + if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk, + router->onion_curve25519_pkey, + ntor_cc_sign_bit)<0) { + log_warn(LD_DIR, "Error converting onion key to ed25519"); + goto err; + } + + if (router_get_hash_impl_helper(s, end-s, "router ", + "\nrouter-sig-ed25519", + ' ', &signed_start, &signed_end) < 0) { + log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor"); + goto err; + } + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, + strlen(ED_DESC_SIGNATURE_PREFIX)); + crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); + crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); + crypto_digest_free(d); + + ed25519_checkable_t check[3]; + int check_ok[3]; + if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for cert."); + goto err; + } + if (tor_cert_get_checkable_sig(&check[1], + ntor_cc_cert, &ntor_cc_pk) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert."); + goto err; + } + + if (ed25519_signature_from_base64(&check[2].signature, + ed_sig_tok->args[0])<0) { + log_warn(LD_DIR, "Couldn't decode ed25519 signature"); + goto err; + } + check[2].pubkey = &cert->signed_key; + check[2].msg = d256; + check[2].len = DIGEST256_LEN; + + if (ed25519_checksig_batch(check_ok, check, 3) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); + goto err; + } + + if (check_tap_onion_key_crosscert( + (const uint8_t*)cc_tap_tok->object_body, + (int)cc_tap_tok->object_size, + router->onion_pkey, + &cert->signing_key, + (const uint8_t*)router->cache_info.identity_digest)<0) { + log_warn(LD_DIR, "Incorrect TAP cross-verification"); + goto err; + } + + /* We check this before adding it to the routerlist. */ + if (cert->valid_until < ntor_cc_cert->valid_until) + router->cert_expiration_time = cert->valid_until; + else + router->cert_expiration_time = ntor_cc_cert->valid_until; + } + } + if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) { /* If there's a fingerprint line, it must match the identity digest. */ char d[DIGEST_LEN]; @@ -1401,12 +1599,26 @@ router_parse_entry_from_string(const char *s, const char *end, } else { log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0])); } + + if (tok->n_args >= 2) { + if (digest256_from_base64(router->cache_info.extra_info_digest256, + tok->args[1]) < 0) { + log_warn(LD_DIR, "Invalid extra info digest256 %s", + escaped(tok->args[1])); + } + } } if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) { router->wants_to_be_hs_dir = 1; } + /* This router accepts tunnelled directory requests via begindir if it has + * an open dirport or it included "tunnelled-dir-server". */ + if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) { + router->supports_tunnelled_dir_requests = 1; + } + tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE); note_crypto_pk_op(VERIFY_RTR); #ifdef COUNT_DISTINCT_DIGESTS @@ -1436,6 +1648,7 @@ router_parse_entry_from_string(const char *s, const char *end, routerinfo_free(router); router = NULL; done: + tor_cert_free(ntor_cc_cert); if (tokens) { SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t)); smartlist_free(tokens); @@ -1502,6 +1715,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, goto err; } + /* XXXX Accept this in position 1 too, and ed identity in position 0. */ tok = smartlist_get(tokens,0); if (tok->tp != K_EXTRA_INFO) { log_warn(LD_DIR,"Entry does not start with \"extra-info\""); @@ -1514,6 +1728,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s); extrainfo->cache_info.signed_descriptor_len = end-s; memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN); + crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256); tor_assert(tok->n_args >= 2); if (!is_legal_nickname(tok->args[0])) { @@ -1536,6 +1751,89 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, goto err; } + { + directory_token_t *ed_sig_tok, *ed_cert_tok; + ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); + ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); + int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok; + if (n_ed_toks != 0 && n_ed_toks != 2) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519/" + "cross-certification support"); + goto err; + } + if (ed_sig_tok) { + tor_assert(ed_cert_tok); + const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok); + if (ed_cert_token_pos != 1) { + /* Accept this in position 0 XXXX */ + log_warn(LD_DIR, "Ed25519 certificate in wrong position"); + goto err; + } + if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) { + log_warn(LD_DIR, "Ed25519 signature in wrong position"); + goto err; + } + if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) { + log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor"); + goto err; + } + + uint8_t d256[DIGEST256_LEN]; + const char *signed_start, *signed_end; + tor_cert_t *cert = tor_cert_parse( + (const uint8_t*)ed_cert_tok->object_body, + ed_cert_tok->object_size); + if (! cert) { + log_warn(LD_DIR, "Couldn't parse ed25519 cert"); + goto err; + } + /* makes sure it gets freed. */ + extrainfo->cache_info.signing_key_cert = cert; + + if (cert->cert_type != CERT_TYPE_ID_SIGNING || + ! cert->signing_key_included) { + log_warn(LD_DIR, "Invalid form for ed25519 cert"); + goto err; + } + + if (router_get_hash_impl_helper(s, end-s, "extra-info ", + "\nrouter-sig-ed25519", + ' ', &signed_start, &signed_end) < 0) { + log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo"); + goto err; + } + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX, + strlen(ED_DESC_SIGNATURE_PREFIX)); + crypto_digest_add_bytes(d, signed_start, signed_end-signed_start); + crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); + crypto_digest_free(d); + + ed25519_checkable_t check[2]; + int check_ok[2]; + if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + log_err(LD_BUG, "Couldn't create 'checkable' for cert."); + goto err; + } + + if (ed25519_signature_from_base64(&check[1].signature, + ed_sig_tok->args[0])<0) { + log_warn(LD_DIR, "Couldn't decode ed25519 signature"); + goto err; + } + check[1].pubkey = &cert->signed_key; + check[1].msg = d256; + check[1].len = DIGEST256_LEN; + + if (ed25519_checksig_batch(check_ok, check, 2) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); + goto err; + } + /* We don't check the certificate expiration time: checking that it + * matches the cert in the router descriptor is adequate. */ + } + } + /* We've checked everything that's covered by the hash. */ can_dl_again = 1; @@ -1775,7 +2073,7 @@ authority_cert_parse_from_string(const char *s, const char **end_of_string) * object (starting with "r " at the start of a line). If none is found, * return the start of the directory footer, or the next directory signature. * If none is found, return the end of the string. */ -static INLINE const char * +static inline const char * find_start_of_next_routerstatus(const char *s) { const char *eos, *footer, *sig; @@ -2008,6 +2306,8 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->is_unnamed = 1; } else if (!strcmp(tok->args[i], "HSDir")) { rs->is_hs_dir = 1; + } else if (!strcmp(tok->args[i], "V2Dir")) { + rs->is_v2_dir = 1; } } } @@ -2015,10 +2315,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, tor_assert(tok->n_args == 1); rs->version_known = 1; if (strcmpstart(tok->args[0], "Tor ")) { - rs->version_supports_microdesc_cache = 1; } else { - rs->version_supports_microdesc_cache = - tor_version_supports_microdescriptors(tok->args[0]); rs->version_supports_extend2_cells = tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha"); } @@ -2091,6 +2388,18 @@ routerstatus_parse_entry_from_string(memarea_t *area, line->microdesc_hash_line = tor_strdup(t->args[0]); vote_rs->microdesc = line; } + if (t->tp == K_ID) { + tor_assert(t->n_args >= 2); + if (!strcmp(t->args[0], "ed25519")) { + vote_rs->has_ed25519_listing = 1; + if (strcmp(t->args[1], "none") && + digest256_from_base64((char*)vote_rs->ed25519_id, + t->args[1])<0) { + log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote"); + goto err; + } + } + } } SMARTLIST_FOREACH_END(t); } else if (flav == FLAV_MICRODESC) { tok = find_opt_by_keyword(tokens, K_M); @@ -2541,7 +2850,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, smartlist_t *rs_tokens = NULL, *footer_tokens = NULL; networkstatus_voter_info_t *voter = NULL; networkstatus_t *ns = NULL; - digests_t ns_digests; + common_digests_t ns_digests; const char *cert, *end_of_header, *end_of_footer, *s_dup = s; directory_token_t *tok; int ok; @@ -2567,7 +2876,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, (ns_type == NS_TYPE_CONSENSUS) ? networkstatus_consensus_token_table : networkstatus_token_table, 0)) { - log_warn(LD_DIR, "Error tokenizing network-status vote header"); + log_warn(LD_DIR, "Error tokenizing network-status header"); goto err; } @@ -2790,7 +3099,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, base16_decode(voter->identity_digest, sizeof(voter->identity_digest), tok->args[1], HEX_DIGEST_LEN) < 0) { log_warn(LD_DIR, "Error decoding identity digest %s in " - "network-status vote.", escaped(tok->args[1])); + "network-status document.", escaped(tok->args[1])); goto err; } if (ns->type != NS_TYPE_CONSENSUS && @@ -2849,7 +3158,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, voter = NULL; } if (smartlist_len(ns->voters) == 0) { - log_warn(LD_DIR, "Missing dir-source elements in a vote networkstatus."); + log_warn(LD_DIR, "Missing dir-source elements in a networkstatus."); goto err; } else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) { log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus."); @@ -2910,11 +3219,27 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN) >= 0) { - log_warn(LD_DIR, "Vote networkstatus entries not sorted by identity " - "digest"); + log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest"); goto err; } } + if (ns_type != NS_TYPE_CONSENSUS) { + digest256map_t *ed_id_map = digest256map_new(); + SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *, + vrs) { + if (! vrs->has_ed25519_listing || + tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN)) + continue; + if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) { + log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not " + "unique"); + digest256map_free(ed_id_map, NULL); + goto err; + } + digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1); + } SMARTLIST_FOREACH_END(vrs); + digest256map_free(ed_id_map, NULL); + } /* Parse footer; check signature. */ footer_tokens = smartlist_new(); @@ -3007,12 +3332,12 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, base16_decode(declared_identity, sizeof(declared_identity), id_hexdigest, HEX_DIGEST_LEN) < 0) { log_warn(LD_DIR, "Error decoding declared identity %s in " - "network-status vote.", escaped(id_hexdigest)); + "network-status document.", escaped(id_hexdigest)); goto err; } if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) { - log_warn(LD_DIR, "ID on signature on network-status vote does not match " - "any declared directory source."); + log_warn(LD_DIR, "ID on signature on network-status document does " + "not match any declared directory source."); goto err; } sig = tor_malloc_zero(sizeof(document_signature_t)); @@ -3022,7 +3347,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest), sk_hexdigest, HEX_DIGEST_LEN) < 0) { log_warn(LD_DIR, "Error decoding declared signing key digest %s in " - "network-status vote.", escaped(sk_hexdigest)); + "network-status document.", escaped(sk_hexdigest)); tor_free(sig); goto err; } @@ -3041,8 +3366,8 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, /* We already parsed a vote with this algorithm from this voter. Use the first one. */ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus " - "that contains two votes from the same voter with the same " - "algorithm. Ignoring the second vote."); + "that contains two signatures from the same voter with the same " + "algorithm. Ignoring the second signature."); tor_free(sig); continue; } @@ -3050,7 +3375,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, if (ns->type != NS_TYPE_CONSENSUS) { if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN, tok, ns->cert->signing_key, 0, - "network-status vote")) { + "network-status document")) { tor_free(sig); goto err; } @@ -3069,7 +3394,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } SMARTLIST_FOREACH_END(_tok); if (! n_signatures) { - log_warn(LD_DIR, "No signatures on networkstatus vote."); + log_warn(LD_DIR, "No signatures on networkstatus document."); goto err; } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) { log_warn(LD_DIR, "Received more than one signature on a " @@ -3120,15 +3445,16 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, return ns; } -/** Return the digests_t that holds the digests of the +/** Return the common_digests_t that holds the digests of the * <b>flavor_name</b>-flavored networkstatus according to the detached - * signatures document <b>sigs</b>, allocating a new digests_t as neeeded. */ -static digests_t * + * signatures document <b>sigs</b>, allocating a new common_digests_t as + * neeeded. */ +static common_digests_t * detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) { - digests_t *d = strmap_get(sigs->digests, flavor_name); + common_digests_t *d = strmap_get(sigs->digests, flavor_name); if (!d) { - d = tor_malloc_zero(sizeof(digests_t)); + d = tor_malloc_zero(sizeof(common_digests_t)); strmap_set(sigs->digests, flavor_name, d); } return d; @@ -3136,7 +3462,7 @@ detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name) /** Return the list of signatures of the <b>flavor_name</b>-flavored * networkstatus according to the detached signatures document <b>sigs</b>, - * allocating a new digests_t as neeeded. */ + * allocating a new common_digests_t as neeeded. */ static smartlist_t * detached_get_signatures(ns_detached_signatures_t *sigs, const char *flavor_name) @@ -3158,7 +3484,7 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos) * networkstatus_parse_vote_from_string(). */ directory_token_t *tok; memarea_t *area = NULL; - digests_t *digests; + common_digests_t *digests; smartlist_t *tokens = smartlist_new(); ns_detached_signatures_t *sigs = @@ -3354,24 +3680,43 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos) * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or * ADDR_POLICY_REJECT) for items that specify no action. * + * Returns NULL on policy errors. + * + * Set *<b>malformed_list</b> to true if the entire policy list should be + * discarded. Otherwise, set it to false, and only this item should be ignored + * on error - the rest of the policy list can continue to be processed and + * used. + * * The addr_policy_t returned by this function can have its address set to * AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair * of AF_INET and AF_INET6 items. */ MOCK_IMPL(addr_policy_t *, -router_parse_addr_policy_item_from_string,(const char *s, int assume_action)) +router_parse_addr_policy_item_from_string,(const char *s, int assume_action, + int *malformed_list)) { directory_token_t *tok = NULL; const char *cp, *eos; - /* Longest possible policy is "accept ffff:ffff:..255/ffff:...255:0-65535". + /* Longest possible policy is + * "accept6 [ffff:ffff:..255]/128:10000-65535", + * which contains a max-length IPv6 address, plus 26 characters. * But note that there can be an arbitrary amount of space between the - * accept and the address:mask/port element. */ + * accept and the address:mask/port element. + * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one + * IPv6 address. But making the buffer shorter might cause valid long lines, + * which parsed in previous versions, to fail to parse in new versions. + * (These lines would have to have excessive amounts of whitespace.) */ char line[TOR_ADDR_BUF_LEN*2 + 32]; addr_policy_t *r; memarea_t *area = NULL; + tor_assert(malformed_list); + *malformed_list = 0; + s = eat_whitespace(s); - if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) { + /* We can only do assume_action on []-quoted IPv6, as "a" (accept) + * and ":" (port separator) are ambiguous */ + if ((*s == '*' || *s == '[' || TOR_ISDIGIT(*s)) && assume_action >= 0) { if (tor_snprintf(line, sizeof(line), "%s %s", assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", s)<0) { log_warn(LD_DIR, "Policy %s is too long.", escaped(s)); @@ -3396,9 +3741,34 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action)) goto err; } + /* Use the extended interpretation of accept/reject *, + * expanding it into an IPv4 wildcard and an IPv6 wildcard. + * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */ r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR); + if (!r) { + goto err; + } + + /* Ensure that accept6/reject6 fields are followed by IPv6 addresses. + * AF_UNSPEC addresses are only permitted on the accept/reject field type. + * Unlike descriptors, torrcs exit policy accept/reject can be followed by + * either an IPv4 or IPv6 address. */ + if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) && + tor_addr_family(&r->addr) != AF_INET6) { + /* This is a non-fatal error, just ignore this one entry. */ + *malformed_list = 0; + log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in " + "exit policy. Ignoring, but continuing to parse rules. (Use " + "accept/reject with IPv4 addresses.)", + tok->n_args == 1 ? tok->args[0] : ""); + addr_policy_free(r); + r = NULL; + goto done; + } + goto done; err: + *malformed_list = 1; r = NULL; done: token_clear(tok); @@ -3415,19 +3785,27 @@ static int router_add_exit_policy(routerinfo_t *router, directory_token_t *tok) { addr_policy_t *newe; + /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */ newe = router_parse_addr_policy(tok, 0); if (!newe) return -1; if (! router->exit_policy) router->exit_policy = smartlist_new(); + /* Ensure that in descriptors, accept/reject fields are followed by + * IPv4 addresses, and accept6/reject6 fields are followed by + * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit + * accept/reject followed by IPv6. */ if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) && tor_addr_family(&newe->addr) == AF_INET) || ((tok->tp == K_ACCEPT || tok->tp == K_REJECT) && tor_addr_family(&newe->addr) == AF_INET6)) { + /* There's nothing the user can do about other relays' descriptors, + * so we don't provide usage advice here. */ log_warn(LD_DIR, "Mismatch between field type and address type in exit " - "policy"); + "policy '%s'. Discarding entire router descriptor.", + tok->n_args == 1 ? tok->args[0] : ""); addr_policy_free(newe); return -1; } @@ -3437,8 +3815,11 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok) return 0; } -/** Given a K_ACCEPT or K_REJECT token and a router, create and return - * a new exit_policy_t corresponding to the token. */ +/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return + * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR + * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by * + * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6 + * policies */ static addr_policy_t * router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) { @@ -3462,6 +3843,13 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) else newe.policy_type = ADDR_POLICY_ACCEPT; + /* accept6/reject6 * produces an IPv6 wildcard address only. + * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */ + if ((fmt_flags & TAPMP_EXTENDED_STAR) + && (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) { + fmt_flags |= TAPMP_STAR_IPV6_ONLY; + } + if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits, &newe.prt_min, &newe.prt_max) < 0) { log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg)); @@ -3471,9 +3859,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags) return addr_policy_get_canonical_entry(&newe); } -/** Parse an exit policy line of the format "accept/reject private:...". +/** Parse an exit policy line of the format "accept[6]/reject[6] private:...". * This didn't exist until Tor 0.1.1.15, so nobody should generate it in * router descriptors until earlier versions are obsolete. + * + * accept/reject and accept6/reject6 private all produce rules for both + * IPv4 and IPv6 addresses. */ static addr_policy_t * router_parse_addr_policy_private(directory_token_t *tok) @@ -3503,6 +3894,13 @@ router_parse_addr_policy_private(directory_token_t *tok) result.prt_min = port_min; result.prt_max = port_max; + if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) { + log_warn(LD_GENERAL, + "'%s' expands into rules which apply to all private IPv4 and " + "IPv6 addresses. (Use accept/reject private:* for IPv4 and " + "IPv6.)", tok->n_args == 1 ? tok->args[0] : ""); + } + return addr_policy_get_canonical_entry(&result); } @@ -3549,7 +3947,7 @@ token_clear(directory_token_t *tok) * Return <b>tok</b> on success, or a new ERR_ token if the token didn't * conform to the syntax we wanted. **/ -static INLINE directory_token_t * +static inline directory_token_t * token_check_object(memarea_t *area, const char *kwd, directory_token_t *tok, obj_syntax o_syn) { @@ -3614,7 +4012,7 @@ token_check_object(memarea_t *area, const char *kwd, * number of parsed elements into the n_args field of <b>tok</b>. Allocate * all storage in <b>area</b>. Return the number of arguments parsed, or * return -1 if there was an insanely high number of arguments. */ -static INLINE int +static inline int get_token_arguments(memarea_t *area, directory_token_t *tok, const char *s, const char *eol) { @@ -4049,7 +4447,7 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest, /** As router_get_hash_impl, but compute all hashes. */ static int -router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests, +router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests, const char *start_str, const char *end_str, char end_c) { @@ -4058,7 +4456,7 @@ router_get_hashes_impl(const char *s, size_t s_len, digests_t *digests, &start,&end)<0) return -1; - if (crypto_digest_all(digests, start, end-start)) { + if (crypto_common_digests(digests, start, end-start)) { log_warn(LD_BUG,"couldn't compute digests"); return -1; } @@ -4184,7 +4582,6 @@ microdescs_parse_from_string(const char *s, const char *eos, } } - if (tokenize_string(area, s, start_of_next_microdesc, tokens, microdesc_token_table, flags)) { log_warn(LD_DIR, "Unparseable microdescriptor"); @@ -4218,6 +4615,26 @@ microdescs_parse_from_string(const char *s, const char *eos, tor_memdup(&k, sizeof(curve25519_public_key_t)); } + smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID); + if (id_lines) { + SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) { + tor_assert(t->n_args >= 2); + if (!strcmp(t->args[0], "ed25519")) { + if (md->ed25519_identity_pkey) { + log_warn(LD_DIR, "Extra ed25519 key in microdesc"); + goto next; + } + ed25519_public_key_t k; + if (ed25519_public_from_base64(&k, t->args[1])<0) { + log_warn(LD_DIR, "Bogus ed25519 key in microdesc"); + goto next; + } + md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k)); + } + } SMARTLIST_FOREACH_END(t); + smartlist_free(id_lines); + } + { smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); if (a_lines) { @@ -4271,14 +4688,6 @@ microdescs_parse_from_string(const char *s, const char *eos, return result; } -/** Return true iff this Tor version can answer directory questions - * about microdescriptors. */ -int -tor_version_supports_microdescriptors(const char *platform) -{ - return tor_version_as_new_as(platform, "0.2.3.1-alpha"); -} - /** Parse the Tor version of the platform string <b>platform</b>, * and compare it to the version in <b>cutoff</b>. Return 1 if * the router is at least as new as the cutoff, else return 0. @@ -4591,8 +5000,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out, tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR); tor_assert(tok == smartlist_get(tokens, 0)); tor_assert(tok->n_args == 1); - if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 || - strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) { + if (!rend_valid_descriptor_id(tok->args[0])) { log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]); goto err; } diff --git a/src/or/routerparse.h b/src/or/routerparse.h index fc21cb1041..c46eb1c0ae 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,12 +14,13 @@ int router_get_router_hash(const char *s, size_t s_len, char *digest); int router_get_dir_hash(const char *s, char *digest); -int router_get_networkstatus_v3_hashes(const char *s, digests_t *digests); +int router_get_networkstatus_v3_hashes(const char *s, + common_digests_t *digests); int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest); #define DIROBJ_MAX_SIG_LEN 256 char *router_get_dirobj_signature(const char *digest, size_t digest_len, - crypto_pk_t *private_key); + const crypto_pk_t *private_key); int router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest, size_t digest_len, @@ -41,10 +42,9 @@ extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end, int cache_copy, struct digest_ri_map_t *routermap, int *can_dl_again_out); MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string, - (const char *s, int assume_action)); + (const char *s, int assume_action, int *malformed_list)); version_status_t tor_version_is_obsolete(const char *myversion, const char *versionlist); -int tor_version_supports_microdescriptors(const char *platform); int tor_version_as_new_as(const char *platform, const char *cutoff); int tor_version_parse(const char *s, tor_version_t *out); int tor_version_compare(tor_version_t *a, tor_version_t *b); @@ -92,5 +92,7 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str, routerstatus_t *rs); #endif +#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" + #endif diff --git a/src/or/routerset.c b/src/or/routerset.c index 99de11ed5e..f260914f4b 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -1,9 +1,16 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file routerset.c + * + * \brief Functions and structures to handle set-type selection of routers + * by name, ID, address, etc. + */ + #define ROUTERSET_PRIVATE #include "or.h" @@ -85,10 +92,13 @@ routerset_parse(routerset_t *target, const char *s, const char *description) int added_countries = 0; char *countryname; smartlist_t *list = smartlist_new(); + int malformed_list; smartlist_split_string(list, s, ",", SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 0); SMARTLIST_FOREACH_BEGIN(list, char *, nick) { addr_policy_t *p; + /* if it doesn't pass our validation, assume it's malformed */ + malformed_list = 1; if (is_legal_hexdigest(nick)) { char d[DIGEST_LEN]; if (*nick == '$') @@ -104,17 +114,25 @@ routerset_parse(routerset_t *target, const char *s, const char *description) description); smartlist_add(target->country_names, countryname); added_countries = 1; - } else if ((strchr(nick,'.') || strchr(nick, '*')) && - (p = router_parse_addr_policy_item_from_string( - nick, ADDR_POLICY_REJECT))) { + } else if ((strchr(nick,'.') || strchr(nick, ':') || strchr(nick, '*')) + && (p = router_parse_addr_policy_item_from_string( + nick, ADDR_POLICY_REJECT, + &malformed_list))) { + /* IPv4 addresses contain '.', IPv6 addresses contain ':', + * and wildcard addresses contain '*'. */ log_debug(LD_CONFIG, "Adding address %s to %s", nick, description); smartlist_add(target->policies, p); - } else { - log_warn(LD_CONFIG, "Entry '%s' in %s is malformed.", nick, - description); + } else if (malformed_list) { + log_warn(LD_CONFIG, "Entry '%s' in %s is malformed. Discarding entire" + " list.", nick, description); r = -1; tor_free(nick); SMARTLIST_DEL_CURRENT(list, nick); + } else { + log_notice(LD_CONFIG, "Entry '%s' in %s is ignored. Using the" + " remainder of the list.", nick, description); + tor_free(nick); + SMARTLIST_DEL_CURRENT(list, nick); } } SMARTLIST_FOREACH_END(nick); policy_expand_unspec(&target->policies); @@ -162,6 +180,17 @@ routerset_is_empty(const routerset_t *set) return !set || smartlist_len(set->list) == 0; } +/** Return the number of entries in <b>set</b>. This does NOT return a + * negative value. */ +int +routerset_len(const routerset_t *set) +{ + if (!set) { + return 0; + } + return smartlist_len(set->list); +} + /** Helper. Return true iff <b>set</b> contains a router based on the other * provided fields. Return higher values for more specific subentries: a * single router is more specific than an address range of routers, which is diff --git a/src/or/routerset.h b/src/or/routerset.h index 8d41de8b6b..c2f7205c3e 100644 --- a/src/or/routerset.h +++ b/src/or/routerset.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -38,6 +38,7 @@ void routerset_subtract_nodes(smartlist_t *out, char *routerset_to_string(const routerset_t *routerset); int routerset_equal(const routerset_t *old, const routerset_t *new); void routerset_free(routerset_t *routerset); +int routerset_len(const routerset_t *set); #ifdef ROUTERSET_PRIVATE STATIC char * routerset_get_countryname(const char *c); diff --git a/src/or/scheduler.c b/src/or/scheduler.c index 931bb6b744..8e4810b199 100644 --- a/src/or/scheduler.c +++ b/src/or/scheduler.c @@ -1,4 +1,4 @@ -/* * Copyright (c) 2013-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2013-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/scheduler.h b/src/or/scheduler.h index 27dd2d8388..94a44a0aa3 100644 --- a/src/or/scheduler.h +++ b/src/or/scheduler.h @@ -1,4 +1,4 @@ -/* * Copyright (c) 2013-2015, The Tor Project, Inc. */ +/* * Copyright (c) 2013-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/or/statefile.c b/src/or/statefile.c index dd1894beb7..9594d9cec3 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -1,14 +1,22 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * \file statefile.c + * + * \brief Handles parsing and encoding the persistent 'state' file that carries + * miscellaneous persistent state between Tor invocations. + */ + #define STATEFILE_PRIVATE #include "or.h" #include "circuitstats.h" #include "config.h" #include "confparse.h" +#include "connection.h" #include "entrynodes.h" #include "hibernate.h" #include "rephist.h" @@ -372,6 +380,12 @@ or_state_load(void) new_state = or_state_new(); } else if (contents) { log_info(LD_GENERAL, "Loaded state from \"%s\"", fname); + /* Warn the user if their clock has been set backwards, + * they could be tricked into using old consensuses */ + time_t apparent_skew = new_state->LastWritten - time(NULL); + if (apparent_skew > 0) + clock_skew_warning(NULL, (long)apparent_skew, 1, LD_GENERAL, + "local state file", fname); } else { log_info(LD_GENERAL, "Initialized state"); } diff --git a/src/or/statefile.h b/src/or/statefile.h index 8c790ea206..b13743481d 100644 --- a/src/or/statefile.h +++ b/src/or/statefile.h @@ -1,7 +1,7 @@ /* 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. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_STATEFILE_H diff --git a/src/or/status.c b/src/or/status.c index 8f7be0aa3c..749cee4edf 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, The Tor Project, Inc. */ +/* Copyright (c) 2010-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -164,24 +164,38 @@ log_accounting(const time_t now, const or_options_t *options) or_state_t *state = get_or_state(); char *acc_rcvd = bytes_to_usage(state->AccountingBytesReadInInterval); char *acc_sent = bytes_to_usage(state->AccountingBytesWrittenInInterval); + char *acc_used = bytes_to_usage(get_accounting_bytes()); uint64_t acc_bytes = options->AccountingMax; char *acc_max; time_t interval_end = accounting_get_end_time(); char end_buf[ISO_TIME_LEN + 1]; char *remaining = NULL; - if (options->AccountingRule == ACCT_SUM) - acc_bytes *= 2; acc_max = bytes_to_usage(acc_bytes); format_local_iso_time(end_buf, interval_end); remaining = secs_to_uptime(interval_end - now); + const char *acc_rule; + switch (options->AccountingRule) { + case ACCT_MAX: acc_rule = "max"; + break; + case ACCT_SUM: acc_rule = "sum"; + break; + case ACCT_OUT: acc_rule = "out"; + break; + case ACCT_IN: acc_rule = "in"; + break; + default: acc_rule = "max"; + break; + } + log_notice(LD_HEARTBEAT, "Heartbeat: Accounting enabled. " - "Sent: %s / %s, Received: %s / %s. The " + "Sent: %s, Received: %s, Used: %s / %s, Rule: %s. The " "current accounting interval ends on %s, in %s.", - acc_sent, acc_max, acc_rcvd, acc_max, end_buf, remaining); + acc_sent, acc_rcvd, acc_used, acc_max, acc_rule, end_buf, remaining); tor_free(acc_rcvd); tor_free(acc_sent); + tor_free(acc_used); tor_free(acc_max); tor_free(remaining); } diff --git a/src/or/status.h b/src/or/status.h index 3dd8206e0f..b97e835037 100644 --- a/src/or/status.h +++ b/src/or/status.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2015, The Tor Project, Inc. */ +/* Copyright (c) 2010-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ #ifndef TOR_STATUS_H diff --git a/src/or/tor_main.c b/src/or/tor_main.c index af03b8c06a..ac32eef559 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -1,6 +1,6 @@ /* Copyright 2001-2004 Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** String describing which Tor Git repository version the source was @@ -27,6 +27,10 @@ int tor_main(int argc, char *argv[]); int main(int argc, char *argv[]) { - return tor_main(argc, argv); + int r = tor_main(argc, argv); + if (r < 0 || r > 255) + return 1; + else + return r; } diff --git a/src/or/torcert.c b/src/or/torcert.c new file mode 100644 index 0000000000..a6a33c675a --- /dev/null +++ b/src/or/torcert.c @@ -0,0 +1,297 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file torcert.c + * + * \brief Implementation for ed25519-signed certificates as used in the Tor + * protocol. + */ + +#include "crypto.h" +#include "torcert.h" +#include "ed25519_cert.h" +#include "torlog.h" +#include "util.h" +#include "compat.h" +#include "link_handshake.h" + +/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519 + * key. + */ +static tor_cert_t * +tor_cert_sign_impl(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + uint8_t signed_key_type, + const uint8_t signed_key_info[32], + time_t now, time_t lifetime, + uint32_t flags) +{ + tor_cert_t *torcert = NULL; + + ed25519_cert_t *cert = ed25519_cert_new(); + cert->cert_type = cert_type; + cert->exp_field = (uint32_t) CEIL_DIV(now + lifetime, 3600); + cert->cert_key_type = signed_key_type; + memcpy(cert->certified_key, signed_key_info, 32); + + if (flags & CERT_FLAG_INCLUDE_SIGNING_KEY) { + ed25519_cert_extension_t *ext = ed25519_cert_extension_new(); + ext->ext_type = CERTEXT_SIGNED_WITH_KEY; + memcpy(ext->un_signing_key, signing_key->pubkey.pubkey, 32); + ed25519_cert_add_ext(cert, ext); + ++cert->n_extensions; + } + + const ssize_t alloc_len = ed25519_cert_encoded_len(cert); + tor_assert(alloc_len > 0); + uint8_t *encoded = tor_malloc(alloc_len); + const ssize_t real_len = ed25519_cert_encode(encoded, alloc_len, cert); + if (real_len < 0) + goto err; + tor_assert(real_len == alloc_len); + tor_assert(real_len > ED25519_SIG_LEN); + uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN); + tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN)); + + ed25519_signature_t signature; + if (ed25519_sign(&signature, encoded, + real_len-ED25519_SIG_LEN, signing_key)<0) { + log_warn(LD_BUG, "Can't sign certificate"); + goto err; + } + memcpy(sig, signature.sig, ED25519_SIG_LEN); + + torcert = tor_cert_parse(encoded, real_len); + if (! torcert) { + log_warn(LD_BUG, "Generated a certificate we cannot parse"); + goto err; + } + + if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) { + log_warn(LD_BUG, "Generated a certificate whose signature we can't check"); + goto err; + } + + tor_free(encoded); + + goto done; + + err: + tor_cert_free(torcert); + torcert = NULL; + done: + ed25519_cert_free(cert); + tor_free(encoded); + return torcert; +} + +/** + * Create and return a new new certificate of type <b>cert_type</b> to + * authenticate <b>signed_key</b> using the key <b>signing_key</b>. The + * certificate should remain valid for at least <b>lifetime</b> seconds after + * <b>now</b>. + * + * If CERT_FLAG_INCLUDE_SIGNING_KEY is set in <b>flags</b>, embed + * the public part of <b>signing_key</b> in the certificate. + */ +tor_cert_t * +tor_cert_create(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + const ed25519_public_key_t *signed_key, + time_t now, time_t lifetime, + uint32_t flags) +{ + return tor_cert_sign_impl(signing_key, cert_type, + SIGNED_KEY_TYPE_ED25519, signed_key->pubkey, + now, lifetime, flags); +} + +/** Release all storage held for <b>cert</b>. */ +void +tor_cert_free(tor_cert_t *cert) +{ + if (! cert) + return; + + if (cert->encoded) + memwipe(cert->encoded, 0, cert->encoded_len); + tor_free(cert->encoded); + + memwipe(cert, 0, sizeof(tor_cert_t)); + tor_free(cert); +} + +/** Parse a certificate encoded with <b>len</b> bytes in <b>encoded</b>. */ +tor_cert_t * +tor_cert_parse(const uint8_t *encoded, const size_t len) +{ + tor_cert_t *cert = NULL; + ed25519_cert_t *parsed = NULL; + ssize_t got_len = ed25519_cert_parse(&parsed, encoded, len); + if (got_len < 0 || (size_t) got_len != len) + goto err; + + cert = tor_malloc_zero(sizeof(tor_cert_t)); + cert->encoded = tor_memdup(encoded, len); + cert->encoded_len = len; + + memcpy(cert->signed_key.pubkey, parsed->certified_key, 32); + cert->valid_until = parsed->exp_field * 3600; + cert->cert_type = parsed->cert_type; + + for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) { + ed25519_cert_extension_t *ext = ed25519_cert_get_ext(parsed, i); + if (ext->ext_type == CERTEXT_SIGNED_WITH_KEY) { + if (cert->signing_key_included) + goto err; + + cert->signing_key_included = 1; + memcpy(cert->signing_key.pubkey, ext->un_signing_key, 32); + } else if (ext->ext_flags & CERTEXT_FLAG_AFFECTS_VALIDATION) { + /* Unrecognized extension with affects_validation set */ + goto err; + } + } + + goto done; + err: + tor_cert_free(cert); + cert = NULL; + done: + ed25519_cert_free(parsed); + return cert; +} + +/** Fill in <b>checkable_out</b> with the information needed to check + * the signature on <b>cert</b> with <b>pubkey</b>. */ +int +tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, + const tor_cert_t *cert, + const ed25519_public_key_t *pubkey) +{ + if (! pubkey) { + if (cert->signing_key_included) + pubkey = &cert->signing_key; + else + return -1; + } + + checkable_out->msg = cert->encoded; + checkable_out->pubkey = pubkey; + tor_assert(cert->encoded_len > ED25519_SIG_LEN); + const size_t signed_len = cert->encoded_len - ED25519_SIG_LEN; + checkable_out->len = signed_len; + memcpy(checkable_out->signature.sig, + cert->encoded + signed_len, ED25519_SIG_LEN); + + return 0; +} + +/** Validates the signature on <b>cert</b> with <b>pubkey</b> relative to the + * current time <b>now</b>. (If <b>now</b> is 0, do not check the expiration + * time.) Return 0 on success, -1 on failure. Sets flags in <b>cert</b> as + * appropriate. + */ +int +tor_cert_checksig(tor_cert_t *cert, + const ed25519_public_key_t *pubkey, time_t now) +{ + ed25519_checkable_t checkable; + int okay; + + if (now && now > cert->valid_until) { + cert->cert_expired = 1; + return -1; + } + + if (tor_cert_get_checkable_sig(&checkable, cert, pubkey) < 0) + return -1; + + if (ed25519_checksig_batch(&okay, &checkable, 1) < 0) { + cert->sig_bad = 1; + return -1; + } else { + cert->sig_ok = 1; + /* Only copy the checkable public key when it is different from the signing + * key of the certificate to avoid undefined behavior. */ + if (cert->signing_key.pubkey != checkable.pubkey->pubkey) { + memcpy(cert->signing_key.pubkey, checkable.pubkey->pubkey, 32); + } + cert->cert_valid = 1; + return 0; + } +} + +/** Return a new copy of <b>cert</b> */ +tor_cert_t * +tor_cert_dup(const tor_cert_t *cert) +{ + tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t)); + if (cert->encoded) + newcert->encoded = tor_memdup(cert->encoded, cert->encoded_len); + return newcert; +} + +/** Return true iff cert1 and cert2 are the same cert. */ +int +tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2) +{ + tor_assert(cert1); + tor_assert(cert2); + return cert1->encoded_len == cert2->encoded_len && + tor_memeq(cert1->encoded, cert2->encoded, cert1->encoded_len); +} + +/** Return true iff cert1 and cert2 are the same cert, or if they are both + * NULL. */ +int +tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2) +{ + if (cert1 == NULL && cert2 == NULL) + return 1; + if (!cert1 || !cert2) + return 0; + return tor_cert_eq(cert1, cert2); +} + +/** Create new cross-certification object to certify <b>ed_key</b> as the + * master ed25519 identity key for the RSA identity key <b>rsa_key</b>. + * Allocates and stores the encoded certificate in *<b>cert</b>, and returns + * the number of bytes stored. Returns negative on error.*/ +ssize_t +tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, + const crypto_pk_t *rsa_key, + time_t expires, + uint8_t **cert) +{ + uint8_t *res; + + rsa_ed_crosscert_t *cc = rsa_ed_crosscert_new(); + memcpy(cc->ed_key, ed_key->pubkey, ED25519_PUBKEY_LEN); + cc->expiration = (uint32_t) CEIL_DIV(expires, 3600); + cc->sig_len = crypto_pk_keysize(rsa_key); + rsa_ed_crosscert_setlen_sig(cc, crypto_pk_keysize(rsa_key)); + + ssize_t alloc_sz = rsa_ed_crosscert_encoded_len(cc); + tor_assert(alloc_sz > 0); + res = tor_malloc_zero(alloc_sz); + ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); + tor_assert(sz > 0 && sz <= alloc_sz); + + const int signed_part_len = 32 + 4; + int siglen = crypto_pk_private_sign(rsa_key, + (char*)rsa_ed_crosscert_getarray_sig(cc), + rsa_ed_crosscert_getlen_sig(cc), + (char*)res, signed_part_len); + tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key)); + tor_assert(siglen <= UINT8_MAX); + cc->sig_len = siglen; + rsa_ed_crosscert_setlen_sig(cc, siglen); + + sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); + rsa_ed_crosscert_free(cc); + *cert = res; + return sz; +} + diff --git a/src/or/torcert.h b/src/or/torcert.h new file mode 100644 index 0000000000..9c819c0abb --- /dev/null +++ b/src/or/torcert.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2014-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TORCERT_H_INCLUDED +#define TORCERT_H_INCLUDED + +#include "crypto_ed25519.h" + +#define SIGNED_KEY_TYPE_ED25519 0x01 + +#define CERT_TYPE_ID_SIGNING 0x04 +#define CERT_TYPE_SIGNING_LINK 0x05 +#define CERT_TYPE_SIGNING_AUTH 0x06 +#define CERT_TYPE_ONION_ID 0x0A + +#define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1 + +/** An ed25519-signed certificate as used throughout the Tor protocol. + **/ +typedef struct tor_cert_st { + /** The key authenticated by this certificate */ + ed25519_public_key_t signed_key; + /** The key that signed this certificate. This value may be unset if the + * certificate has never been checked, and didn't include its own key. */ + ed25519_public_key_t signing_key; + /** A time after which this certificate will no longer be valid. */ + time_t valid_until; + + /** The encoded representation of this certificate */ + uint8_t *encoded; + /** The length of <b>encoded</b> */ + size_t encoded_len; + + /** One of CERT_TYPE_... */ + uint8_t cert_type; + /** True iff we received a signing key embedded in this certificate */ + unsigned signing_key_included : 1; + /** True iff we checked the signature and found it bad */ + unsigned sig_bad : 1; + /** True iff we checked the signature and found it correct */ + unsigned sig_ok : 1; + /** True iff we checked the signature and first found that the cert + * had expired */ + unsigned cert_expired : 1; + /** True iff we checked the signature and found the whole cert valid */ + unsigned cert_valid : 1; +} tor_cert_t; + +tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + const ed25519_public_key_t *signed_key, + time_t now, time_t lifetime, + uint32_t flags); + +tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); + +void tor_cert_free(tor_cert_t *cert); + +int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, + const tor_cert_t *out, + const ed25519_public_key_t *pubkey); + +int tor_cert_checksig(tor_cert_t *cert, + const ed25519_public_key_t *pubkey, time_t now); + +tor_cert_t *tor_cert_dup(const tor_cert_t *cert); +int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); +int tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); + +ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, + const crypto_pk_t *rsa_key, + time_t expires, + uint8_t **cert); + +#endif + diff --git a/src/or/transports.c b/src/or/transports.c index 6f07054ea8..1b8b1e678c 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2015, The Tor Project, Inc. */ +/* Copyright (c) 2011-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -105,7 +105,7 @@ static process_environment_t * create_managed_proxy_environment(const managed_proxy_t *mp); -static INLINE int proxy_configuration_finished(const managed_proxy_t *mp); +static inline int proxy_configuration_finished(const managed_proxy_t *mp); static void handle_finished_proxy(managed_proxy_t *mp); static void parse_method_error(const char *line, int is_server_method); @@ -713,7 +713,7 @@ register_client_proxy(const managed_proxy_t *mp) } /** Register the transports of managed proxy <b>mp</b>. */ -static INLINE void +static inline void register_proxy(const managed_proxy_t *mp) { if (mp->is_server) @@ -828,7 +828,7 @@ handle_finished_proxy(managed_proxy_t *mp) /** Return true if the configuration of the managed proxy <b>mp</b> is finished. */ -static INLINE int +static inline int proxy_configuration_finished(const managed_proxy_t *mp) { return (mp->conf_state == PT_PROTO_CONFIGURED || @@ -1100,7 +1100,7 @@ parse_smethod_line(const char *line, managed_proxy_t *mp) smartlist_add(mp->transports, transport); - /* For now, notify the user so that he knows where the server + /* For now, notify the user so that they know where the server transport is listening. */ log_info(LD_CONFIG, "Server transport %s at %s:%d.", method_name, address, (int)port); @@ -1388,6 +1388,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp) } else { smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT="); } + + /* All new versions of tor will keep stdin open, so PTs can use it + * as a reliable termination detection mechanism. + */ + smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1"); } else { /* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the * TOR_PT_PROXY line. diff --git a/src/or/transports.h b/src/or/transports.h index 7c69941496..7de90dcbec 100644 --- a/src/or/transports.h +++ b/src/or/transports.h @@ -1,6 +1,6 @@ /* Copyright (c) 2003-2004, Roger Dingledine * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2015, The Tor Project, Inc. */ + * Copyright (c) 2007-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** |