diff options
Diffstat (limited to 'src/or')
87 files changed, 6452 insertions, 1449 deletions
diff --git a/src/or/addressmap.c b/src/or/addressmap.c index f7544abacc..85a6434f4a 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -376,29 +376,38 @@ addressmap_rewrite(char *address, size_t maxlen, char *addr_orig = tor_strdup(address); char *log_addr_orig = NULL; + /* We use a loop here to limit the total number of rewrites we do, + * so that we can't hit an infinite loop. */ for (rewrites = 0; rewrites < 16; rewrites++) { int exact_match = 0; log_addr_orig = tor_strdup(escaped_safe_str_client(address)); + /* First check to see if there's an exact match for this address */ ent = strmap_get(addressmap, address); if (!ent || !ent->new_address) { + /* And if we don't have an exact match, try to check whether + * we have a pattern-based match. + */ ent = addressmap_match_superdomains(address); } else { if (ent->src_wildcard && !ent->dst_wildcard && !strcasecmp(address, ent->new_address)) { - /* This is a rule like *.example.com example.com, and we just got - * "example.com" */ + /* This is a rule like "rewrite *.example.com to example.com", and we + * just got "example.com". Instead of calling it an infinite loop, + * call it complete. */ goto done; } - exact_match = 1; } if (!ent || !ent->new_address) { + /* We still have no match at all. We're done! */ goto done; } + /* Check wither the flags we were passed tell us not to use this + * mapping. */ switch (ent->source) { case ADDRMAPSRC_DNS: { @@ -431,6 +440,8 @@ addressmap_rewrite(char *address, size_t maxlen, goto done; } + /* Now fill in the address with the new address. That might be via + * appending some new stuff to the end, or via just replacing it. */ if (ent->dst_wildcard && !exact_match) { strlcat(address, ".", maxlen); strlcat(address, ent->new_address, maxlen); @@ -438,6 +449,7 @@ addressmap_rewrite(char *address, size_t maxlen, strlcpy(address, ent->new_address, maxlen); } + /* Is this now a .exit address? If so, remember where we got it.*/ if (!strcmpend(address, ".exit") && strcmpend(addr_orig, ".exit") && exit_source == ADDRMAPSRC_NONE) { @@ -774,7 +786,7 @@ parse_virtual_addr_network(const char *val, sa_family_t family, const int ipv6 = (family == AF_INET6); tor_addr_t addr; maskbits_t bits; - const int max_bits = ipv6 ? 40 : 16; + const int max_prefix_bits = ipv6 ? 104 : 16; virtual_addr_conf_t *conf = ipv6 ? &virtaddr_conf_ipv6 : &virtaddr_conf_ipv4; if (!val || val[0] == '\0') { @@ -804,10 +816,10 @@ parse_virtual_addr_network(const char *val, sa_family_t family, } #endif - if (bits > max_bits) { + if (bits > max_prefix_bits) { if (msg) tor_asprintf(msg, "VirtualAddressNetwork%s expects a /%d " - "network or larger",ipv6?"IPv6":"", max_bits); + "network or larger",ipv6?"IPv6":"", max_prefix_bits); return -1; } diff --git a/src/or/buffers.c b/src/or/buffers.c index 3198572392..8981fd283b 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -6,10 +6,22 @@ /** * \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. Buffers are implemented - * as linked lists of memory chunks. + * \brief Implements a generic buffer interface. + * + * A buf_t is a (fairly) opaque byte-oriented FIFO that can read to or flush + * from memory, sockets, file descriptors, TLS connections, or another buf_t. + * Buffers are implemented as linked lists of memory chunks. + * + * All socket-backed and TLS-based connection_t objects have a pair of + * buffers: one for incoming data, and one for outcoming data. These are fed + * and drained from functions in connection.c, trigged by events that are + * monitored in main.c. + * + * This module has basic support for reading and writing on buf_t objects. It + * also contains specialized functions for handling particular protocols + * on a buf_t backend, including SOCKS (used in connection_edge.c), Tor cells + * (used in connection_or.c and channeltls.c), HTTP (used in directory.c), and + * line-oriented communication (used in control.c). **/ #define BUFFERS_PRIVATE #include "or.h" @@ -70,12 +82,33 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, #define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0]) +/* We leave this many NUL bytes at the end of the buffer. */ +#define SENTINEL_LEN 4 + +/* Header size plus NUL bytes at the end */ +#define CHUNK_OVERHEAD (CHUNK_HEADER_LEN + SENTINEL_LEN) + /** Return the number of bytes needed to allocate a chunk to hold * <b>memlen</b> bytes. */ -#define CHUNK_ALLOC_SIZE(memlen) (CHUNK_HEADER_LEN + (memlen)) +#define CHUNK_ALLOC_SIZE(memlen) (CHUNK_OVERHEAD + (memlen)) /** Return the number of usable bytes in a chunk allocated with * malloc(<b>memlen</b>). */ -#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - CHUNK_HEADER_LEN) +#define CHUNK_SIZE_WITH_ALLOC(memlen) ((memlen) - CHUNK_OVERHEAD) + +#define DEBUG_SENTINEL + +#ifdef DEBUG_SENTINEL +#define DBG_S(s) s +#else +#define DBG_S(s) (void)0 +#endif + +#define CHUNK_SET_SENTINEL(chunk, alloclen) do { \ + uint8_t *a = (uint8_t*) &(chunk)->mem[(chunk)->memlen]; \ + DBG_S(uint8_t *b = &((uint8_t*)(chunk))[(alloclen)-SENTINEL_LEN]); \ + DBG_S(tor_assert(a == b)); \ + memset(a,0,SENTINEL_LEN); \ + } while (0) /** 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. */ @@ -132,6 +165,7 @@ chunk_new_with_alloc_size(size_t alloc) ch->memlen = CHUNK_SIZE_WITH_ALLOC(alloc); total_bytes_allocated_in_chunks += alloc; ch->data = &ch->mem[0]; + CHUNK_SET_SENTINEL(ch, alloc); return ch; } @@ -141,18 +175,20 @@ static inline chunk_t * chunk_grow(chunk_t *chunk, size_t sz) { off_t offset; - size_t memlen_orig = chunk->memlen; + const size_t memlen_orig = chunk->memlen; + const size_t orig_alloc = CHUNK_ALLOC_SIZE(memlen_orig); + const size_t new_alloc = CHUNK_ALLOC_SIZE(sz); tor_assert(sz > chunk->memlen); offset = chunk->data - chunk->mem; - chunk = tor_realloc(chunk, CHUNK_ALLOC_SIZE(sz)); + chunk = tor_realloc(chunk, new_alloc); chunk->memlen = sz; chunk->data = chunk->mem + offset; #ifdef DEBUG_CHUNK_ALLOC - tor_assert(chunk->DBG_alloc == CHUNK_ALLOC_SIZE(memlen_orig)); - chunk->DBG_alloc = CHUNK_ALLOC_SIZE(sz); + tor_assert(chunk->DBG_alloc == orig_alloc); + chunk->DBG_alloc = new_alloc; #endif - total_bytes_allocated_in_chunks += - CHUNK_ALLOC_SIZE(sz) - CHUNK_ALLOC_SIZE(memlen_orig); + total_bytes_allocated_in_chunks += new_alloc - orig_alloc; + CHUNK_SET_SENTINEL(chunk, new_alloc); return chunk; } @@ -166,9 +202,12 @@ 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 size_t preferred_chunk_size(size_t target) { + tor_assert(target <= SIZE_T_CEILING - CHUNK_OVERHEAD); + if (CHUNK_ALLOC_SIZE(target) >= MAX_CHUNK_ALLOC) + return CHUNK_ALLOC_SIZE(target); size_t sz = MIN_CHUNK_ALLOC; while (CHUNK_SIZE_WITH_ALLOC(sz) < target) { sz <<= 1; diff --git a/src/or/buffers.h b/src/or/buffers.h index 275867c70a..52b21d5885 100644 --- a/src/or/buffers.h +++ b/src/or/buffers.h @@ -65,6 +65,7 @@ void assert_buf_ok(buf_t *buf); 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); void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz); +STATIC size_t preferred_chunk_size(size_t target); #define DEBUG_CHUNK_ALLOC /** A single chunk on a buffer. */ diff --git a/src/or/channel.c b/src/or/channel.c index 87fa721089..af5810788c 100644 --- a/src/or/channel.c +++ b/src/or/channel.c @@ -8,6 +8,32 @@ * transfer cells from Tor instance to Tor instance. * Currently, there is only one implementation of the channel abstraction: in * channeltls.c. + * + * Channels are a higher-level abstraction than or_connection_t: In general, + * any means that two Tor relays use to exchange cells, or any means that a + * relay and a client use to exchange cells, is a channel. + * + * Channels differ from pluggable transports in that they do not wrap an + * underlying protocol over which cells are transmitted: they <em>are</em> the + * underlying protocol. + * + * This module defines the generic parts of the channel_t interface, and + * provides the machinery necessary for specialized implementations to be + * created. At present, there is one specialized implementation in + * channeltls.c, which uses connection_or.c to send cells over a TLS + * connection. + * + * Every channel implementation is responsible for being able to transmit + * cells that are added to it with channel_write_cell() and related functions, + * and to receive incoming cells with the channel_queue_cell() and related + * functions. See the channel_t documentation for more information. + * + * When new cells arrive on a channel, they are passed to cell handler + * functions, which can be set by channel_set_cell_handlers() + * functions. (Tor's cell handlers are in command.c.) + * + * Tor flushes cells to channels from relay.c in + * channel_flush_from_first_active_circuit(). **/ /* @@ -838,7 +864,7 @@ channel_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -878,7 +904,7 @@ channel_listener_free(channel_listener_t *chan_l) tor_assert(!(chan_l->registered)); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * We're in CLOSED or ERROR, so the incoming channel queue is already @@ -916,7 +942,7 @@ channel_force_free(channel_t *chan) } /* Call a free method if there is one */ - if (chan->free) chan->free(chan); + if (chan->free_fn) chan->free_fn(chan); channel_clear_remote_end(chan); @@ -958,7 +984,7 @@ channel_listener_force_free(channel_listener_t *chan_l) chan_l); /* Call a free method if there is one */ - if (chan_l->free) chan_l->free(chan_l); + if (chan_l->free_fn) chan_l->free_fn(chan_l); /* * The incoming list just gets emptied and freed; we request close on @@ -1712,7 +1738,7 @@ channel_get_cell_queue_entry_size(channel_t *chan, cell_queue_entry_t *q) rv = get_cell_network_size(chan->wide_circ_ids); break; default: - tor_assert(1); + tor_assert_nonfatal_unreached_once(); } return rv; @@ -1812,45 +1838,58 @@ channel_write_cell_queue_entry(channel_t *chan, cell_queue_entry_t *q) } } -/** - * Write a cell to a channel +/** Write a generic cell type to a channel * - * Write a fixed-length cell to a channel using the write_cell() method. - * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); - * it is called by the transport-independent code to deliver a cell to a - * channel for transmission. + * Write a generic cell to a channel. It is called by channel_write_cell(), + * channel_write_var_cell() and channel_write_packed_cell() in order to reduce + * code duplication. Notice that it takes cell as pointer of type void, + * this can be dangerous because no type check is performed. */ void -channel_write_cell(channel_t *chan, cell_t *cell) +channel_write_cell_generic_(channel_t *chan, const char *cell_type, + void *cell, cell_queue_entry_t *q) { - cell_queue_entry_t q; tor_assert(chan); tor_assert(cell); if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding cell_t %p on closing channel %p with " - "global ID "U64_FORMAT, cell, chan, + log_debug(LD_CHANNEL, "Discarding %c %p on closing channel %p with " + "global ID "U64_FORMAT, *cell_type, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); tor_free(cell); return; } - log_debug(LD_CHANNEL, - "Writing cell_t %p to channel %p with global ID " - U64_FORMAT, + "Writing %c %p to channel %p with global ID " + U64_FORMAT, *cell_type, cell, chan, U64_PRINTF_ARG(chan->global_identifier)); - q.type = CELL_QUEUE_FIXED; - q.u.fixed.cell = cell; - channel_write_cell_queue_entry(chan, &q); - + channel_write_cell_queue_entry(chan, q); /* Update the queue size estimate */ channel_update_xmit_queue_size(chan); } /** + * Write a cell to a channel + * + * Write a fixed-length cell to a channel using the write_cell() method. + * This is equivalent to the pre-channels connection_or_write_cell_to_buf(); + * it is called by the transport-independent code to deliver a cell to a + * channel for transmission. + */ + +void +channel_write_cell(channel_t *chan, cell_t *cell) +{ + cell_queue_entry_t q; + q.type = CELL_QUEUE_FIXED; + q.u.fixed.cell = cell; + channel_write_cell_generic_(chan, "cell_t", cell, &q); +} + +/** * Write a packed cell to a channel * * Write a packed cell to a channel using the write_cell() method. This is @@ -1862,30 +1901,9 @@ void channel_write_packed_cell(channel_t *chan, packed_cell_t *packed_cell) { cell_queue_entry_t q; - - tor_assert(chan); - tor_assert(packed_cell); - - if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding packed_cell_t %p on closing channel %p " - "with global ID "U64_FORMAT, packed_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - packed_cell_free(packed_cell); - return; - } - - log_debug(LD_CHANNEL, - "Writing packed_cell_t %p to channel %p with global ID " - U64_FORMAT, - packed_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - q.type = CELL_QUEUE_PACKED; q.u.packed.packed_cell = packed_cell; - channel_write_cell_queue_entry(chan, &q); - - /* Update the queue size estimate */ - channel_update_xmit_queue_size(chan); + channel_write_cell_generic_(chan, "packed_cell_t", packed_cell, &q); } /** @@ -1901,30 +1919,9 @@ void channel_write_var_cell(channel_t *chan, var_cell_t *var_cell) { cell_queue_entry_t q; - - tor_assert(chan); - tor_assert(var_cell); - - if (CHANNEL_IS_CLOSING(chan)) { - log_debug(LD_CHANNEL, "Discarding var_cell_t %p on closing channel %p " - "with global ID "U64_FORMAT, var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - var_cell_free(var_cell); - return; - } - - log_debug(LD_CHANNEL, - "Writing var_cell_t %p to channel %p with global ID " - U64_FORMAT, - var_cell, chan, - U64_PRINTF_ARG(chan->global_identifier)); - q.type = CELL_QUEUE_VAR; q.u.var.var_cell = var_cell; - channel_write_cell_queue_entry(chan, &q); - - /* Update the queue size estimate */ - channel_update_xmit_queue_size(chan); + channel_write_cell_generic_(chan, "var_cell_t", var_cell, &q); } /** @@ -3223,9 +3220,10 @@ channel_free_all(void) channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest) + const char *id_digest, + const ed25519_public_key_t *ed_id) { - return channel_tls_connect(addr, port, id_digest); + return channel_tls_connect(addr, port, id_digest, ed_id); } /** diff --git a/src/or/channel.h b/src/or/channel.h index 78e1b71014..7e7b2ec899 100644 --- a/src/or/channel.h +++ b/src/or/channel.h @@ -90,7 +90,7 @@ struct channel_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_t *); + void (*free_fn)(channel_t *); /** Close an open channel */ void (*close)(channel_t *); /** Describe the transport subclass for this channel */ @@ -273,7 +273,7 @@ struct channel_listener_s { /* Methods implemented by the lower layer */ /** Free a channel */ - void (*free)(channel_listener_t *); + void (*free_fn)(channel_listener_t *); /** Close an open channel */ void (*close)(channel_listener_t *); /** Describe the transport subclass for this channel */ @@ -382,6 +382,9 @@ struct cell_queue_entry_s { STATIC int chan_cell_queue_len(const chan_cell_queue_t *queue); STATIC void cell_queue_entry_free(cell_queue_entry_t *q, int handed_off); + +void channel_write_cell_generic_(channel_t *chan, const char *cell_type, + void *cell, cell_queue_entry_t *q); #endif /* Channel operations for subclasses and internal use only */ @@ -486,7 +489,8 @@ int channel_send_destroy(circid_t circ_id, channel_t *chan, */ channel_t * channel_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest); + const char *id_digest, + const ed25519_public_key_t *ed_id); channel_t * channel_get_for_extend(const char *digest, const tor_addr_t *target_addr, diff --git a/src/or/channeltls.c b/src/or/channeltls.c index a62f80ef91..9fb309d0fd 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -6,6 +6,28 @@ * * \brief A concrete subclass of channel_t using or_connection_t to transfer * cells between Tor instances. + * + * This module fills in the various function pointers in channel_t, to + * implement the channel_tls_t channels as used in Tor today. These channels + * are created from channel_tls_connect() and + * channel_tls_handle_incoming(). Each corresponds 1:1 to or_connection_t + * object, as implemented in connection_or.c. These channels transmit cells + * to the underlying or_connection_t by calling + * connection_or_write_*_cell_to_buf(), and receive cells from the underlying + * or_connection_t when connection_or_process_cells_from_inbuf() calls + * channel_tls_handle_*_cell(). + * + * Here we also implement the server (responder) side of the v3+ Tor link + * handshake, which uses CERTS and AUTHENTICATE cell to negotiate versions, + * exchange expected and observed IP and time information, and bootstrap a + * level of authentication higher than we have gotten on the raw TLS + * handshake. + * + * NOTE: Since there is currently only one type of channel, there are probably + * more than a few cases where functionality that is currently in + * channeltls.c, connection_or.c, and channel.c ought to be divided up + * differently. The right time to do this is probably whenever we introduce + * our next channel type. **/ /* @@ -33,6 +55,7 @@ #include "router.h" #include "routerlist.h" #include "scheduler.h" +#include "torcert.h" /** How many CELL_PADDING cells have we received, ever? */ uint64_t stats_n_padding_cells_processed = 0; @@ -117,7 +140,7 @@ channel_tls_common_init(channel_tls_t *tlschan) chan->state = CHANNEL_STATE_OPENING; chan->close = channel_tls_close_method; chan->describe_transport = channel_tls_describe_transport_method; - chan->free = channel_tls_free_method; + chan->free_fn = channel_tls_free_method; chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method; chan->get_remote_addr = channel_tls_get_remote_addr_method; chan->get_remote_descr = channel_tls_get_remote_descr_method; @@ -148,8 +171,10 @@ channel_tls_common_init(channel_tls_t *tlschan) channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest) + const char *id_digest, + const ed25519_public_key_t *ed_id) { + (void) ed_id; // XXXX not fully used yet channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); channel_t *chan = &(tlschan->base_); @@ -176,7 +201,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, channel_mark_outgoing(chan); /* Set up or_connection stuff */ - tlschan->conn = connection_or_connect(addr, port, id_digest, tlschan); + tlschan->conn = connection_or_connect(addr, port, id_digest, ed_id, tlschan); /* connection_or_connect() will fill in tlschan->conn */ if (!(tlschan->conn)) { chan->reason_for_closing = CHANNEL_CLOSE_FOR_ERROR; @@ -576,7 +601,7 @@ channel_tls_get_remote_descr_method(channel_t *chan, int flags) break; default: /* Something's broken in channel.c */ - tor_assert(1); + tor_assert_nonfatal_unreached_once(); } } else { strlcpy(buf, "(No connection)", sizeof(buf)); @@ -645,7 +670,7 @@ channel_tls_is_canonical_method(channel_t *chan, int req) break; default: /* This shouldn't happen; channel.c is broken if it does */ - tor_assert(1); + tor_assert_nonfatal_unreached_once(); } } /* else return 0 for tlschan->conn == NULL */ @@ -1617,7 +1642,10 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) if (!(chan->conn->handshake_state->authenticated)) { tor_assert(tor_digest_is_zero( (const char*)(chan->conn->handshake_state-> - authenticated_peer_id))); + authenticated_rsa_peer_id))); + tor_assert(tor_mem_is_zero( + (const char*)(chan->conn->handshake_state-> + authenticated_ed25519_peer_id.pubkey), 32)); channel_set_circid_type(TLS_CHAN_TO_BASE(chan), NULL, chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); @@ -1625,7 +1653,8 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) &(chan->conn->base_.addr), chan->conn->base_.port, (const char*)(chan->conn->handshake_state-> - authenticated_peer_id), + authenticated_rsa_peer_id), + NULL, // XXXX Ed key 0); } } @@ -1722,6 +1751,41 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) assert_connection_ok(TO_CONN(chan->conn),time(NULL)); } +/** Types of certificates that we know how to parse from CERTS cells. Each + * type corresponds to a different encoding format. */ +typedef enum cert_encoding_t { + CERT_ENCODING_UNKNOWN, /**< We don't recognize this. */ + CERT_ENCODING_X509, /**< It's an RSA key, signed with RSA, encoded in x509. + * (Actually, it might not be RSA. We test that later.) */ + CERT_ENCODING_ED25519, /**< It's something signed with an Ed25519 key, + * encoded asa a tor_cert_t.*/ + CERT_ENCODING_RSA_CROSSCERT, /**< It's an Ed key signed with an RSA key. */ +} cert_encoding_t; + +/** + * Given one of the certificate type codes used in a CERTS cell, + * return the corresponding cert_encoding_t that we should use to parse + * the certificate. + */ +static cert_encoding_t +certs_cell_typenum_to_cert_type(int typenum) +{ + switch (typenum) { + case CERTTYPE_RSA1024_ID_LINK: + case CERTTYPE_RSA1024_ID_ID: + case CERTTYPE_RSA1024_ID_AUTH: + return CERT_ENCODING_X509; + case CERTTYPE_ED_ID_SIGN: + case CERTTYPE_ED_SIGN_LINK: + case CERTTYPE_ED_SIGN_AUTH: + return CERT_ENCODING_ED25519; + case CERTTYPE_RSA1024_ID_EDID: + return CERT_ENCODING_RSA_CROSSCERT; + default: + return CERT_ENCODING_UNKNOWN; + } +} + /** * Process a CERTS cell from a channel. * @@ -1741,14 +1805,21 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) STATIC void channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) { -#define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024 - tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1]; +#define MAX_CERT_TYPE_WANTED CERTTYPE_RSA1024_ID_EDID + /* These arrays will be sparse, since a cert type can be at most one + * of ed/x509 */ + tor_x509_cert_t *x509_certs[MAX_CERT_TYPE_WANTED + 1]; + tor_cert_t *ed_certs[MAX_CERT_TYPE_WANTED + 1]; + uint8_t *rsa_ed_cc_cert = NULL; + size_t rsa_ed_cc_cert_len = 0; + int n_certs, i; certs_cell_t *cc = NULL; int send_netinfo = 0; - memset(certs, 0, sizeof(certs)); + memset(x509_certs, 0, sizeof(x509_certs)); + memset(ed_certs, 0, sizeof(ed_certs)); tor_assert(cell); tor_assert(chan); tor_assert(chan->conn); @@ -1792,77 +1863,146 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) if (cert_type > MAX_CERT_TYPE_WANTED) continue; + const cert_encoding_t ct = certs_cell_typenum_to_cert_type(cert_type); + switch (ct) { + default: + case CERT_ENCODING_UNKNOWN: + break; + case CERT_ENCODING_X509: { + tor_x509_cert_t *x509_cert = tor_x509_cert_decode(cert_body, cert_len); + if (!x509_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 (x509_certs[cert_type]) { + tor_x509_cert_free(x509_cert); + ERR("Duplicate x509 certificate"); + } else { + x509_certs[cert_type] = x509_cert; + } + } + break; + } + case CERT_ENCODING_ED25519: { + tor_cert_t *ed_cert = tor_cert_parse(cert_body, cert_len); + if (!ed_cert) { + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "Received undecodable Ed certificate " + "in CERTS cell from %s:%d", + safe_str(chan->conn->base_.address), + chan->conn->base_.port); + } else { + if (ed_certs[cert_type]) { + tor_cert_free(ed_cert); + ERR("Duplicate Ed25519 certificate"); + } else { + ed_certs[cert_type] = ed_cert; + } + } + break; + } - 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 { - certs[cert_type] = cert; + case CERT_ENCODING_RSA_CROSSCERT: { + if (rsa_ed_cc_cert) { + ERR("Duplicate RSA->Ed25519 crosscert"); + } else { + rsa_ed_cc_cert = tor_memdup(cert_body, cert_len); + rsa_ed_cc_cert_len = cert_len; + } + break; } } } - 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]; + /* Move the certificates we (might) want into the handshake_state->certs + * structure. */ + tor_x509_cert_t *id_cert = x509_certs[CERTTYPE_RSA1024_ID_ID]; + tor_x509_cert_t *auth_cert = x509_certs[CERTTYPE_RSA1024_ID_AUTH]; + tor_x509_cert_t *link_cert = x509_certs[CERTTYPE_RSA1024_ID_LINK]; + chan->conn->handshake_state->certs->auth_cert = auth_cert; + chan->conn->handshake_state->certs->link_cert = link_cert; + chan->conn->handshake_state->certs->id_cert = id_cert; + x509_certs[CERTTYPE_RSA1024_ID_ID] = + x509_certs[CERTTYPE_RSA1024_ID_AUTH] = + x509_certs[CERTTYPE_RSA1024_ID_LINK] = NULL; + + tor_cert_t *ed_id_sign = ed_certs[CERTTYPE_ED_ID_SIGN]; + tor_cert_t *ed_sign_link = ed_certs[CERTTYPE_ED_SIGN_LINK]; + tor_cert_t *ed_sign_auth = ed_certs[CERTTYPE_ED_SIGN_AUTH]; + chan->conn->handshake_state->certs->ed_id_sign = ed_id_sign; + chan->conn->handshake_state->certs->ed_sign_link = ed_sign_link; + chan->conn->handshake_state->certs->ed_sign_auth = ed_sign_auth; + ed_certs[CERTTYPE_ED_ID_SIGN] = + ed_certs[CERTTYPE_ED_SIGN_LINK] = + ed_certs[CERTTYPE_ED_SIGN_AUTH] = NULL; + + chan->conn->handshake_state->certs->ed_rsa_crosscert = rsa_ed_cc_cert; + chan->conn->handshake_state->certs->ed_rsa_crosscert_len = + rsa_ed_cc_cert_len; + rsa_ed_cc_cert = NULL; + + int severity; + /* Note that this warns more loudly about time and validity if we were + * _trying_ to connect to an authority, not necessarily if we _did_ connect + * to one. */ + if (chan->conn->handshake_state->started_here && + router_digest_is_trusted_dir(TLS_CHAN_TO_BASE(chan)->identity_digest)) + severity = LOG_WARN; + else + severity = LOG_PROTOCOL_WARN; + + const ed25519_public_key_t *checked_ed_id = NULL; + const common_digests_t *checked_rsa_id = NULL; + or_handshake_certs_check_both(severity, + chan->conn->handshake_state->certs, + chan->conn->tls, + time(NULL), + &checked_ed_id, + &checked_rsa_id); + + if (!checked_rsa_id) + ERR("Invalid certificate chain!"); if (chan->conn->handshake_state->started_here) { - int severity; - if (! (id_cert && link_cert)) - ERR("The certs we wanted were missing"); - /* Okay. We should be able to check the certificates now. */ - if (! tor_tls_cert_matches_key(chan->conn->tls, link_cert)) { - ERR("The link certificate didn't match the TLS public key"); - } - /* Note that this warns more loudly about time and validity if we were - * _trying_ to connect to an authority, not necessarily if we _did_ connect - * to one. */ - if (router_digest_is_trusted_dir( - TLS_CHAN_TO_BASE(chan)->identity_digest)) - severity = LOG_WARN; - else - severity = LOG_PROTOCOL_WARN; - - if (! tor_tls_cert_is_valid(severity, link_cert, id_cert, 0)) - ERR("The link certificate was not valid"); - if (! tor_tls_cert_is_valid(severity, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); + /* No more information is needed. */ chan->conn->handshake_state->authenticated = 1; + chan->conn->handshake_state->authenticated_rsa = 1; { - const common_digests_t *id_digests = - tor_x509_cert_get_id_digests(id_cert); + const common_digests_t *id_digests = checked_rsa_id; crypto_pk_t *identity_rcvd; if (!id_digests) ERR("Couldn't compute digests for key in ID cert"); identity_rcvd = tor_tls_cert_get_key(id_cert); - if (!identity_rcvd) - ERR("Internal error: Couldn't get RSA key from ID cert."); - memcpy(chan->conn->handshake_state->authenticated_peer_id, + if (!identity_rcvd) { + ERR("Couldn't get RSA key from ID cert."); + } + memcpy(chan->conn->handshake_state->authenticated_rsa_peer_id, id_digests->d[DIGEST_SHA1], DIGEST_LEN); channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd, chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); crypto_pk_free(identity_rcvd); } + if (checked_ed_id) { + chan->conn->handshake_state->authenticated_ed25519 = 1; + memcpy(&chan->conn->handshake_state->authenticated_ed25519_peer_id, + checked_ed_id, sizeof(ed25519_public_key_t)); + } + if (connection_or_client_learned_peer_id(chan->conn, - chan->conn->handshake_state->authenticated_peer_id) < 0) + chan->conn->handshake_state->authenticated_rsa_peer_id, + checked_ed_id) < 0) ERR("Problem setting or checking peer id"); log_info(LD_OR, - "Got some good certificates from %s:%d: Authenticated it.", - safe_str(chan->conn->base_.address), chan->conn->base_.port); - - chan->conn->handshake_state->id_cert = id_cert; - certs[OR_CERT_TYPE_ID_1024] = NULL; + "Got some good certificates from %s:%d: Authenticated it with " + "RSA%s", + safe_str(chan->conn->base_.address), chan->conn->base_.port, + checked_ed_id ? " and Ed25519" : ""); if (!public_server_mode(get_options())) { /* If we initiated the connection and we are not a public server, we @@ -1871,25 +2011,14 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) send_netinfo = 1; } } else { - if (! (id_cert && auth_cert)) - ERR("The certs we wanted were missing"); - - /* Remember these certificates so we can check an AUTHENTICATE cell */ - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, auth_cert, id_cert, 1)) - ERR("The authentication certificate was not valid"); - if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, id_cert, id_cert, 1)) - ERR("The ID certificate was not valid"); - + /* We can't call it authenticated till we see an AUTHENTICATE cell. */ log_info(LD_OR, - "Got some good certificates from %s:%d: " + "Got some good RSA%s certificates from %s:%d. " "Waiting for AUTHENTICATE.", + checked_ed_id ? " and Ed25519" : "", safe_str(chan->conn->base_.address), chan->conn->base_.port); /* XXXX check more stuff? */ - - chan->conn->handshake_state->id_cert = id_cert; - chan->conn->handshake_state->auth_cert = auth_cert; - certs[OR_CERT_TYPE_ID_1024] = certs[OR_CERT_TYPE_AUTH_1024] = NULL; } chan->conn->handshake_state->received_certs_cell = 1; @@ -1903,9 +2032,13 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } err: - for (unsigned u = 0; u < ARRAY_LENGTH(certs); ++u) { - tor_x509_cert_free(certs[u]); + for (unsigned u = 0; u < ARRAY_LENGTH(x509_certs); ++u) { + tor_x509_cert_free(x509_certs[u]); + } + for (unsigned u = 0; u < ARRAY_LENGTH(ed_certs); ++u) { + tor_cert_free(ed_certs[u]); } + tor_free(rsa_ed_cc_cert); certs_cell_free(cc); #undef ERR } @@ -1962,8 +2095,12 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) /* Now see if there is an authentication type we can use */ 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; + if (authchallenge_type_is_supported(authtype)) { + if (use_type == -1 || + authchallenge_type_is_better(authtype, use_type)) { + use_type = authtype; + } + } } chan->conn->handshake_state->received_auth_challenge = 1; @@ -1978,9 +2115,10 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) if (use_type >= 0) { log_info(LD_OR, "Got an AUTH_CHALLENGE cell from %s:%d: Sending " - "authentication", + "authentication type %d", safe_str(chan->conn->base_.address), - chan->conn->base_.port); + chan->conn->base_.port, + use_type); if (connection_or_send_authenticate_cell(chan->conn, use_type) < 0) { log_warn(LD_OR, @@ -2021,9 +2159,11 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) { - uint8_t expected[V3_AUTH_FIXED_PART_LEN+256]; + var_cell_t *expected_cell = NULL; const uint8_t *auth; int authlen; + int authtype; + int bodylen; tor_assert(cell); tor_assert(chan); @@ -2036,6 +2176,7 @@ channel_tls_process_authenticate_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); \ + var_cell_free(expected_cell); \ return; \ } while (0) @@ -2053,9 +2194,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) } if (!(chan->conn->handshake_state->received_certs_cell)) ERR("We never got a certs cell"); - if (chan->conn->handshake_state->auth_cert == NULL) - ERR("We never got an authentication certificate"); - if (chan->conn->handshake_state->id_cert == NULL) + if (chan->conn->handshake_state->certs->id_cert == NULL) ERR("We never got an identity certificate"); if (cell->payload_len < 4) ERR("Cell was way too short"); @@ -2067,8 +2206,9 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) if (4 + len > cell->payload_len) ERR("Authenticator was truncated"); - if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) + if (! authchallenge_type_is_supported(type)) ERR("Authenticator type was not recognized"); + authtype = type; auth += 4; authlen = len; @@ -2077,25 +2217,55 @@ 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"); - 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) + expected_cell = connection_or_compute_authenticate_cell_body( + chan->conn, authtype, NULL, NULL, 1); + if (! expected_cell) ERR("Couldn't compute expected AUTHENTICATE cell body"); - if (tor_memneq(expected, auth, bodylen)) + int sig_is_rsa; + if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET || + authtype == AUTHTYPE_RSA_SHA256_RFC5705) { + bodylen = V3_AUTH_BODY_LEN; + sig_is_rsa = 1; + } else { + tor_assert(authtype == AUTHTYPE_ED25519_SHA256_RFC5705); + /* Our earlier check had better have made sure we had room + * for an ed25519 sig (inadvertently) */ + tor_assert(V3_AUTH_BODY_LEN > ED25519_SIG_LEN); + bodylen = authlen - ED25519_SIG_LEN; + sig_is_rsa = 0; + } + if (expected_cell->payload_len != bodylen+4) { + ERR("Expected AUTHENTICATE cell body len not as expected."); + } + + /* Length of random part. */ + if (BUG(bodylen < 24)) { + // LCOV_EXCL_START + ERR("Bodylen is somehow less than 24, which should really be impossible"); + // LCOV_EXCL_STOP + } + + if (tor_memneq(expected_cell->payload+4, auth, bodylen-24)) ERR("Some field in the AUTHENTICATE cell body was not as expected"); - { + if (sig_is_rsa) { + if (chan->conn->handshake_state->certs->ed_id_sign != NULL) + ERR("RSA-signed AUTHENTICATE response provided with an ED25519 cert"); + + if (chan->conn->handshake_state->certs->auth_cert == NULL) + ERR("We never got an RSA authentication certificate"); + crypto_pk_t *pk = tor_tls_cert_get_key( - chan->conn->handshake_state->auth_cert); + chan->conn->handshake_state->certs->auth_cert); char d[DIGEST256_LEN]; char *signed_data; size_t keysize; int signed_len; - if (!pk) - ERR("Internal error: couldn't get RSA key from AUTH cert."); + if (! pk) { + ERR("Couldn't get RSA key from AUTH cert."); + } crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); keysize = crypto_pk_keysize(pk); @@ -2106,7 +2276,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) crypto_pk_free(pk); if (signed_len < 0) { tor_free(signed_data); - ERR("Signature wasn't valid"); + ERR("RSA signature wasn't valid"); } if (signed_len < DIGEST256_LEN) { tor_free(signed_data); @@ -2119,22 +2289,45 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) ERR("Signature did not match data to be signed."); } tor_free(signed_data); + } else { + if (chan->conn->handshake_state->certs->ed_id_sign == NULL) + ERR("We never got an Ed25519 identity certificate."); + if (chan->conn->handshake_state->certs->ed_sign_auth == NULL) + ERR("We never got an Ed25519 authentication certificate."); + + const ed25519_public_key_t *authkey = + &chan->conn->handshake_state->certs->ed_sign_auth->signed_key; + ed25519_signature_t sig; + tor_assert(authlen > ED25519_SIG_LEN); + memcpy(&sig.sig, auth + authlen - ED25519_SIG_LEN, ED25519_SIG_LEN); + if (ed25519_checksig(&sig, auth, authlen - ED25519_SIG_LEN, authkey)<0) { + ERR("Ed25519 signature wasn't valid."); + } } /* Okay, we are authenticated. */ chan->conn->handshake_state->received_authenticate = 1; chan->conn->handshake_state->authenticated = 1; + chan->conn->handshake_state->authenticated_rsa = 1; chan->conn->handshake_state->digest_received_data = 0; { - crypto_pk_t *identity_rcvd = - tor_tls_cert_get_key(chan->conn->handshake_state->id_cert); - const common_digests_t *id_digests = - tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert); + tor_x509_cert_t *id_cert = chan->conn->handshake_state->certs->id_cert; + crypto_pk_t *identity_rcvd = tor_tls_cert_get_key(id_cert); + const common_digests_t *id_digests = tor_x509_cert_get_id_digests(id_cert); + const ed25519_public_key_t *ed_identity_received = NULL; + + if (! sig_is_rsa) { + chan->conn->handshake_state->authenticated_ed25519 = 1; + ed_identity_received = + &chan->conn->handshake_state->certs->ed_id_sign->signing_key; + memcpy(&chan->conn->handshake_state->authenticated_ed25519_peer_id, + ed_identity_received, sizeof(ed25519_public_key_t)); + } /* This must exist; we checked key type when reading the cert. */ tor_assert(id_digests); - memcpy(chan->conn->handshake_state->authenticated_peer_id, + memcpy(chan->conn->handshake_state->authenticated_rsa_peer_id, id_digests->d[DIGEST_SHA1], DIGEST_LEN); channel_set_circid_type(TLS_CHAN_TO_BASE(chan), identity_rcvd, @@ -2145,15 +2338,19 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) &(chan->conn->base_.addr), chan->conn->base_.port, (const char*)(chan->conn->handshake_state-> - authenticated_peer_id), + authenticated_rsa_peer_id), + ed_identity_received, 0); log_info(LD_OR, - "Got an AUTHENTICATE cell from %s:%d: Looks good.", + "Got an AUTHENTICATE cell from %s:%d, type %d: Looks good.", safe_str(chan->conn->base_.address), - chan->conn->base_.port); + chan->conn->base_.port, + authtype); } + var_cell_free(expected_cell); + #undef ERR } diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 8b5863a461..729e595615 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -29,7 +29,8 @@ struct channel_tls_s { #endif /* TOR_CHANNEL_INTERNAL_ */ channel_t * channel_tls_connect(const tor_addr_t *addr, uint16_t port, - const char *id_digest); + const char *id_digest, + const ed25519_public_key_t *ed_id); channel_listener_t * channel_tls_get_listener(void); channel_listener_t * channel_tls_start_listener(void); channel_t * channel_tls_handle_incoming(or_connection_t *orconn); diff --git a/src/or/circpathbias.c b/src/or/circpathbias.c index 9f93e737f7..6ee69aac1e 100644 --- a/src/or/circpathbias.c +++ b/src/or/circpathbias.c @@ -11,6 +11,14 @@ * 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. + * + * This code is currently configured in a warning-only mode, though false + * positives appear to be rare in practice. There is also support for + * disabling really bad guards, but it's quite experimental and may have bad + * anonymity effects. + * + * The information here is associated with the entry_guard_t object for + * each guard, and stored persistently in the state file. */ #include "or.h" diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 14d40150db..0881f231aa 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -9,6 +9,20 @@ * * \brief Implements the details of building circuits (by chosing paths, * constructing/sending create/extend cells, and so on). + * + * On the client side, this module handles launching circuits. Circuit + * launches are srtarted from circuit_establish_circuit(), called from + * circuit_launch_by_extend_info()). To choose the path the circuit will + * take, onion_extend_cpath() calls into a maze of node selection functions. + * + * Once the circuit is ready to be launched, the first hop is treated as a + * special case with circuit_handle_first_hop(), since it might need to open a + * channel. As the channel opens, and later as CREATED and RELAY_EXTENDED + * cells arrive, the client will invoke circuit_send_next_onion_skin() to send + * CREATE or RELAY_EXTEND cells. + * + * On the server side, this module also handles the logic of responding to + * RELAY_EXTEND requests, using circuit_extend(). **/ #define CIRCUITBUILD_PRIVATE @@ -28,6 +42,7 @@ #include "connection_edge.h" #include "connection_or.h" #include "control.h" +#include "crypto.h" #include "directory.h" #include "entrynodes.h" #include "main.h" @@ -38,14 +53,14 @@ #include "onion_tap.h" #include "onion_fast.h" #include "policies.h" -#include "transports.h" #include "relay.h" +#include "rendcommon.h" #include "rephist.h" #include "router.h" #include "routerlist.h" #include "routerparse.h" #include "routerset.h" -#include "crypto.h" +#include "transports.h" static channel_t * channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port, @@ -58,7 +73,6 @@ static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); static int count_acceptable_nodes(smartlist_t *routers); static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); -static int circuits_can_use_ntor(void); /** This function tries to get a channel to the specified endpoint, * and then calls command_setup_channel() to give it the right @@ -70,7 +84,9 @@ channel_connect_for_circuit(const tor_addr_t *addr, uint16_t port, { channel_t *chan; - chan = channel_connect(addr, port, id_digest); + chan = channel_connect(addr, port, id_digest, + NULL // XXXX Ed25519 id. + ); if (chan) command_setup_channel(chan); return chan; @@ -365,7 +381,7 @@ circuit_rep_hist_note_result(origin_circuit_t *circ) } while (hop!=circ->cpath); } -/** Return 1 iff at least one node in circ's cpath supports ntor. */ +/** Return 1 iff every node in circ's cpath definitely supports ntor. */ static int circuit_cpath_supports_ntor(const origin_circuit_t *circ) { @@ -373,16 +389,19 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) cpath = head = circ->cpath; do { - if (cpath->extend_info && - !tor_mem_is_zero( - (const char*)cpath->extend_info->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) - return 1; + /* if the extend_info is missing, we can't tell if it supports ntor */ + if (!cpath->extend_info) { + return 0; + } + /* if the key is blank, it definitely doesn't support ntor */ + if (!extend_info_supports_ntor(cpath->extend_info)) { + return 0; + } cpath = cpath->next; } while (cpath != head); - return 0; + return 1; } /** Pick all the entries in our cpath. Stop and return 0 when we're @@ -390,41 +409,61 @@ circuit_cpath_supports_ntor(const origin_circuit_t *circ) static int onion_populate_cpath(origin_circuit_t *circ) { - int n_tries = 0; - const int using_ntor = circuits_can_use_ntor(); + int r = 0; -#define MAX_POPULATE_ATTEMPTS 32 + /* onion_extend_cpath assumes these are non-NULL */ + tor_assert(circ); + tor_assert(circ->build_state); - while (1) { - int r = onion_extend_cpath(circ); + while (r == 0) { + r = onion_extend_cpath(circ); if (r < 0) { log_info(LD_CIRC,"Generating cpath hop failed."); return -1; } - if (r == 1) { - /* This circuit doesn't need/shouldn't be forced to have an ntor hop */ - if (circ->build_state->desired_path_len <= 1 || ! using_ntor) - return 0; + } - /* This circuit has an ntor hop. great! */ - if (circuit_cpath_supports_ntor(circ)) - return 0; + /* The path is complete */ + tor_assert(r == 1); - /* No node in the circuit supports ntor. Have we already tried too many - * times? */ - if (++n_tries >= MAX_POPULATE_ATTEMPTS) - break; + /* Does every node in this path support ntor? */ + int path_supports_ntor = circuit_cpath_supports_ntor(circ); - /* Clear the path and retry */ - circuit_clear_cpath(circ); + /* We would like every path to support ntor, but we have to allow for some + * edge cases. */ + tor_assert(circuit_get_cpath_len(circ)); + if (circuit_can_use_tap(circ)) { + /* Circuits from clients to intro points, and hidden services to + * rend points do not support ntor, because the hidden service protocol + * does not include ntor onion keys. This is also true for Tor2web clients + * and Single Onion Services. */ + return 0; + } + + if (circuit_get_cpath_len(circ) == 1) { + /* Allow for bootstrapping: when we're fetching directly from a fallback, + * authority, or bridge, we have no way of knowing its ntor onion key + * before we connect to it. So instead, we try connecting, and end up using + * CREATE_FAST. */ + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + const node_t *node = node_get_by_id( + circ->cpath->extend_info->identity_digest); + /* If we don't know the node and its descriptor, we must be bootstrapping. + */ + if (!node || !node_has_descriptor(node)) { + return 0; } } - log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop " - "circuit with at least one node that supports ntor.", - MAX_POPULATE_ATTEMPTS, - circ->build_state->desired_path_len); - return -1; + if (BUG(!path_supports_ntor)) { + /* If we're building a multi-hop path, and it's not one of the HS or + * bootstrapping exceptions, and it doesn't support ntor, something has + * gone wrong. */ + return -1; + } + + return 0; } /** Create and return a new origin circuit. Initialize its purpose and @@ -757,10 +796,13 @@ should_use_create_fast_for_circuit(origin_circuit_t *circ) tor_assert(circ->cpath); tor_assert(circ->cpath->extend_info); - if (!circ->cpath->extend_info->onion_key) - return 1; /* our hand is forced: only a create_fast will work. */ + if (!circuit_has_usable_onion_key(circ)) { + /* We don't have ntor, and we don't have or can't use TAP, + * so our hand is forced: only a create_fast will work. */ + return 1; + } if (public_server_mode(options)) { - /* We're a server, and we know an onion key. We can choose. + /* We're a server, and we have a usable onion key. We can choose. * Prefer to blend our circuit into the other circuits we are * creating on behalf of others. */ return 0; @@ -785,62 +827,56 @@ circuit_timeout_want_to_count_circ(origin_circuit_t *circ) && circ->build_state->desired_path_len == DEFAULT_ROUTE_LEN; } -/** Return true if the ntor handshake is enabled in the configuration, or if - * it's been set to "auto" in the configuration and it's enabled in the - * consensus. */ -static int -circuits_can_use_ntor(void) -{ - const or_options_t *options = get_options(); - if (options->UseNTorHandshake != -1) - return options->UseNTorHandshake; - return networkstatus_get_param(NULL, "UseNTorHandshake", 0, 0, 1); -} - /** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> * directly, and set *<b>cell_type_out</b> and *<b>handshake_type_out</b> - * accordingly. */ + * accordingly. + * Note that TAP handshakes in CREATE cells are only used for direct + * connections: + * - from Tor2web to intro points not in the client's consensus, and + * - from Single Onions to rend points not in the service's consensus. + * This is checked in onion_populate_cpath. */ static void circuit_pick_create_handshake(uint8_t *cell_type_out, uint16_t *handshake_type_out, const extend_info_t *ei) { - /* XXXX029 Remove support for deciding to use TAP. */ - if (!tor_mem_is_zero((const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN) && - circuits_can_use_ntor()) { + /* torspec says: In general, clients SHOULD use CREATE whenever they are + * using the TAP handshake, and CREATE2 otherwise. */ + if (extend_info_supports_ntor(ei)) { *cell_type_out = CELL_CREATE2; *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; - return; + } else { + /* XXXX030 Remove support for deciding to use TAP and EXTEND. */ + *cell_type_out = CELL_CREATE; + *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP; } - - *cell_type_out = CELL_CREATE; - *handshake_type_out = ONION_HANDSHAKE_TYPE_TAP; } -/** Decide whether to use a TAP or ntor handshake for connecting to <b>ei</b> - * directly, and set *<b>handshake_type_out</b> accordingly. Decide whether, - * in extending through <b>node</b> to do so, we should use an EXTEND2 or an - * EXTEND cell to do so, and set *<b>cell_type_out</b> and - * *<b>create_cell_type_out</b> accordingly. */ +/** Decide whether to use a TAP or ntor handshake for extending to <b>ei</b> + * and set *<b>handshake_type_out</b> accordingly. Decide whether we should + * use an EXTEND2 or an EXTEND cell to do so, and set *<b>cell_type_out</b> + * and *<b>create_cell_type_out</b> accordingly. + * Note that TAP handshakes in EXTEND cells are only used: + * - from clients to intro points, and + * - from hidden services to rend points. + * This is checked in onion_populate_cpath. + */ static void circuit_pick_extend_handshake(uint8_t *cell_type_out, uint8_t *create_cell_type_out, uint16_t *handshake_type_out, - const node_t *node_prev, const extend_info_t *ei) { uint8_t t; circuit_pick_create_handshake(&t, handshake_type_out, ei); - /* XXXX029 Remove support for deciding to use TAP. */ - if (node_prev && - *handshake_type_out != ONION_HANDSHAKE_TYPE_TAP && - (node_has_curve25519_onion_key(node_prev) || - (node_prev->rs && node_prev->rs->version_supports_extend2_cells))) { + /* torspec says: Clients SHOULD use the EXTEND format whenever sending a TAP + * handshake... In other cases, clients SHOULD use EXTEND2. */ + if (*handshake_type_out != ONION_HANDSHAKE_TYPE_TAP) { *cell_type_out = RELAY_COMMAND_EXTEND2; *create_cell_type_out = CELL_CREATE2; } else { + /* XXXX030 Remove support for deciding to use TAP and EXTEND. */ *cell_type_out = RELAY_COMMAND_EXTEND; *create_cell_type_out = CELL_CREATE; } @@ -996,15 +1032,10 @@ circuit_send_next_onion_skin(origin_circuit_t *circ) return - END_CIRC_REASON_INTERNAL; } - { - const node_t *prev_node; - prev_node = node_get_by_id(hop->prev->extend_info->identity_digest); - circuit_pick_extend_handshake(&ec.cell_type, - &ec.create_cell.cell_type, - &ec.create_cell.handshake_type, - prev_node, - hop->extend_info); - } + circuit_pick_extend_handshake(&ec.cell_type, + &ec.create_cell.cell_type, + &ec.create_cell.handshake_type, + hop->extend_info); tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr); ec.orport_ipv4.port = hop->extend_info->port; @@ -1826,13 +1857,32 @@ pick_rendezvous_node(router_crn_flags_t flags) flags |= CRN_ALLOW_INVALID; #ifdef ENABLE_TOR2WEB_MODE + /* We want to connect directly to the node if we can */ + router_crn_flags_t direct_flags = flags; + direct_flags |= CRN_PREF_ADDR; + direct_flags |= CRN_DIRECT_CONN; + /* The user wants us to pick specific RPs. */ if (options->Tor2webRendezvousPoints) { - const node_t *tor2web_rp = pick_tor2web_rendezvous_node(flags, options); + const node_t *tor2web_rp = pick_tor2web_rendezvous_node(direct_flags, + options); if (tor2web_rp) { return tor2web_rp; } - /* Else, if no tor2web RP was found, fall back to choosing a random node */ + } + + /* Else, if no direct, preferred tor2web RP was found, fall back to choosing + * a random direct node */ + const node_t *node = router_choose_random_node(NULL, options->ExcludeNodes, + direct_flags); + /* Return the direct node (if found), or log a message and fall back to an + * indirect connection. */ + if (node) { + return node; + } else { + log_info(LD_REND, + "Unable to find a random rendezvous point that is reachable via " + "a direct connection, falling back to a 3-hop path."); } #endif @@ -1967,7 +2017,9 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei) cpath_build_state_t *state = circ->build_state; if (state->onehop_tunnel) { - log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel."); + log_debug(LD_CIRC, "Launching a one-hop circuit for dir tunnel%s.", + (rend_allow_non_anonymous_connection(get_options()) ? + ", or intro or rendezvous connection" : "")); state->desired_path_len = 1; } else { int r = new_route_len(circ->base_.purpose, exit_ei, nodelist_get_list()); @@ -2058,15 +2110,18 @@ count_acceptable_nodes(smartlist_t *nodes) if (! node->is_running) // log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); continue; + /* XXX This clause makes us count incorrectly: if AllowInvalidRouters + * allows this node in some places, then we're getting an inaccurate + * count. For now, be conservative and don't count it. But later we + * should try to be smarter. */ if (! node->is_valid) // log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); continue; if (! node_has_descriptor(node)) continue; - /* XXX This clause makes us count incorrectly: if AllowInvalidRouters - * allows this node in some places, then we're getting an inaccurate - * count. For now, be conservative and don't count it. But later we - * should try to be smarter. */ + /* The node has a descriptor, so we can just check the ntor key directly */ + if (!node_has_curve25519_onion_key(node)) + continue; ++num; } SMARTLIST_FOREACH_END(node); @@ -2177,7 +2232,6 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state) * This is an incomplete fix, but is no worse than the previous behaviour, * and only applies to minimal, testing tor networks * (so it's no less secure) */ - /*XXXX++ use the using_as_guard flag to accomplish this.*/ if (options->UseEntryGuards && (!options->TestingTorNetwork || smartlist_len(nodelist_get_list()) > smartlist_len(get_entry_guards()) @@ -2356,6 +2410,14 @@ extend_info_from_node(const node_t *node, int for_direct_connect) log_warn(LD_CIRC, "Could not choose valid address for %s", node->ri ? node->ri->nickname : node->rs->nickname); + /* Every node we connect or extend to must support ntor */ + if (!node_has_curve25519_onion_key(node)) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Attempted to create extend_info for a node that does not support " + "ntor: %s", node_describe(node)); + return NULL; + } + if (valid_addr && node->ri) return extend_info_new(node->ri->nickname, node->identity, @@ -2441,3 +2503,66 @@ extend_info_addr_is_allowed(const tor_addr_t *addr) return 0; } +/* Does ei have a valid TAP key? */ +int +extend_info_supports_tap(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid TAP keys are not NULL */ + return ei->onion_key != NULL; +} + +/* Does ei have a valid ntor key? */ +int +extend_info_supports_ntor(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid ntor keys have at least one non-zero byte */ + return !tor_mem_is_zero( + (const char*)ei->curve25519_onion_key.public_key, + CURVE25519_PUBKEY_LEN); +} + +/* Is circuit purpose allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +static int +circuit_purpose_can_use_tap_impl(uint8_t purpose) +{ + return (purpose == CIRCUIT_PURPOSE_S_CONNECT_REND || + purpose == CIRCUIT_PURPOSE_C_INTRODUCING); +} + +/* Is circ allowed to use the deprecated TAP encryption protocol? + * The hidden service protocol still uses TAP for some connections, because + * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ +int +circuit_can_use_tap(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (circuit_purpose_can_use_tap_impl(circ->base_.purpose) && + extend_info_supports_tap(circ->cpath->extend_info)); +} + +/* Does circ have an onion key which it's allowed to use? */ +int +circuit_has_usable_onion_key(const origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(circ->cpath); + tor_assert(circ->cpath->extend_info); + return (extend_info_supports_ntor(circ->cpath->extend_info) || + circuit_can_use_tap(circ)); +} + +/* Does ei have an onion key which it would prefer to use? + * Currently, we prefer ntor keys*/ +int +extend_info_has_preferred_onion_key(const extend_info_t* ei) +{ + tor_assert(ei); + return extend_info_supports_ntor(ei); +} + diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index 7f5fd511a9..1244601f71 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -54,6 +54,11 @@ 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); +int extend_info_supports_tap(const extend_info_t* ei); +int extend_info_supports_ntor(const extend_info_t* ei); +int circuit_can_use_tap(const origin_circuit_t *circ); +int circuit_has_usable_onion_key(const origin_circuit_t *circ); +int extend_info_has_preferred_onion_key(const extend_info_t* ei); 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 7c6dbc53e9..dee103e36a 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -7,7 +7,48 @@ /** * \file circuitlist.c * - * \brief Manage the global circuit list, and looking up circuits within it. + * \brief Manage global structures that list and index circuits, and + * look up circuits within them. + * + * One of the most frequent operations in Tor occurs every time that + * a relay cell arrives on a channel. When that happens, we need to + * find which circuit it is associated with, based on the channel and the + * circuit ID in the relay cell. + * + * To handle that, we maintain a global list of circuits, and a hashtable + * mapping [channel,circID] pairs to circuits. Circuits are added to and + * removed from this mapping using circuit_set_p_circid_chan() and + * circuit_set_n_circid_chan(). To look up a circuit from this map, most + * callers should use circuit_get_by_circid_channel(), though + * circuit_get_by_circid_channel_even_if_marked() is appropriate under some + * circumstances. + * + * We also need to allow for the possibility that we have blocked use of a + * circuit ID (because we are waiting to send a DESTROY cell), but the + * circuit is not there any more. For that case, we allow placeholder + * entries in the table, using channel_mark_circid_unusable(). + * + * To efficiently handle a channel that has just opened, we also maintain a + * list of the circuits waiting for channels, so we can attach them as + * needed without iterating through the whole list of circuits, using + * circuit_get_all_pending_on_channel(). + * + * In this module, we also handle the list of circuits that have been + * marked for close elsewhere, and close them as needed. (We use this + * "mark now, close later" pattern here and elsewhere to avoid + * unpredictable recursion if we closed every circuit immediately upon + * realizing it needed to close.) See circuit_mark_for_close() for the + * mark function, and circuit_close_all_marked() for the close function. + * + * For hidden services, we need to be able to look up introduction point + * circuits and rendezvous circuits by cookie, key, etc. These are + * currently handled with linear searches in + * circuit_get_ready_rend_circuit_by_rend_data(), + * circuit_get_next_by_pk_and_purpose(), and with hash lookups in + * circuit_get_rendezvous() and circuit_get_intro_point(). + * + * This module is also the entry point for our out-of-memory handler + * logic, which was originally circuit-focused. **/ #define CIRCUITLIST_PRIVATE #include "or.h" @@ -1549,6 +1590,14 @@ circuit_set_intro_point_digest(or_circuit_t *circ, const uint8_t *digest) * cannibalize. * * If !CIRCLAUNCH_NEED_UPTIME, prefer returning non-uptime circuits. + * + * To "cannibalize" a circuit means to extend it an extra hop, and use it + * for some other purpose than we had originally intended. We do this when + * we want to perform some low-bandwidth task at a specific relay, and we + * would like the circuit to complete as soon as possible. (If we were going + * to use a lot of bandwidth, we wouldn't want a circuit with an extra hop. + * If we didn't care about circuit completion latency, we would just build + * a new circuit.) */ origin_circuit_t * circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, @@ -1623,7 +1672,8 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, return best; } -/** Return the number of hops in circuit's path. */ +/** Return the number of hops in circuit's path. If circ has no entries, + * or is NULL, returns 0. */ int circuit_get_cpath_len(origin_circuit_t *circ) { @@ -1639,7 +1689,8 @@ circuit_get_cpath_len(origin_circuit_t *circ) } /** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there - * aren't that many hops in the list. */ + * aren't that many hops in the list. <b>hopnum</b> starts at 1. + * Returns NULL if <b>hopnum</b> is 0 or negative. */ crypt_path_t * circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum) { @@ -1928,8 +1979,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/circuitmux.c b/src/or/circuitmux.c index 038904e68a..96a3647aab 100644 --- a/src/or/circuitmux.c +++ b/src/or/circuitmux.c @@ -4,49 +4,20 @@ /** * \file circuitmux.c * \brief Circuit mux/cell selection abstraction - **/ - -#include "or.h" -#include "channel.h" -#include "circuitlist.h" -#include "circuitmux.h" -#include "relay.h" - -/* - * Private typedefs for circuitmux.c - */ - -/* - * Map of muxinfos for circuitmux_t to use; struct is defined below (name - * of struct must match HT_HEAD line). - */ -typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t; - -/* - * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to - * break the hash table code). - */ -typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t; - -/* - * Anything the mux wants to store per-circuit in the map; right now just - * a count of queued cells. - */ - -typedef struct circuit_muxinfo_s circuit_muxinfo_t; - -/* - * Structures for circuitmux.c - */ - -/* - * A circuitmux is a collection of circuits; it tracks which subset - * of the attached circuits are 'active' (i.e., have cells available - * to transmit) and how many cells on each. It expoes three distinct + * + * A circuitmux is responsible for <b>MU</b>ltiple<b>X</b>ing all of the + * circuits that are writing on a single channel. It keeps track of which of + * these circuits has something to write (aka, "active" circuits), and which + * one should write next. A circuitmux corresponds 1:1 with a channel. + * + * There can be different implementations of the circuitmux's rules (which + * decide which circuit is next to write). + * + * A circuitmux exposes three distinct * interfaces to other components: * * To channels, which each have a circuitmux_t, the supported operations - * are: + * (invoked from relay.c) are: * * circuitmux_get_first_active_circuit(): * @@ -74,7 +45,9 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t; * * circuitmux_set_num_cells(): * - * Set the circuitmux's cell counter for this circuit. + * Set the circuitmux's cell counter for this circuit. One of + * circuitmuc_clear_num_cells() or circuitmux_set_num_cells() MUST be + * called when the number of cells queued on a circuit changes. * * See circuitmux.h for the circuitmux_policy_t data structure, which contains * a table of function pointers implementing a circuit selection policy, and @@ -94,7 +67,39 @@ typedef struct circuit_muxinfo_s circuit_muxinfo_t; * * Install a policy on a circuitmux_t; the appropriate callbacks will be * made to attach all existing circuits to the new policy. - * + **/ + +#include "or.h" +#include "channel.h" +#include "circuitlist.h" +#include "circuitmux.h" +#include "relay.h" + +/* + * Private typedefs for circuitmux.c + */ + +/* + * Map of muxinfos for circuitmux_t to use; struct is defined below (name + * of struct must match HT_HEAD line). + */ +typedef struct chanid_circid_muxinfo_map chanid_circid_muxinfo_map_t; + +/* + * Hash table entry (yeah, calling it chanid_circid_muxinfo_s seems to + * break the hash table code). + */ +typedef struct chanid_circid_muxinfo_t chanid_circid_muxinfo_t; + +/* + * Anything the mux wants to store per-circuit in the map; right now just + * a count of queued cells. + */ + +typedef struct circuit_muxinfo_s circuit_muxinfo_t; + +/* + * Structures for circuitmux.c */ struct circuitmux_s { diff --git a/src/or/circuitmux_ewma.c b/src/or/circuitmux_ewma.c index b784a140ac..0219459cdb 100644 --- a/src/or/circuitmux_ewma.c +++ b/src/or/circuitmux_ewma.c @@ -4,10 +4,34 @@ /** * \file circuitmux_ewma.c * \brief EWMA circuit selection as a circuitmux_t policy + * + * The "EWMA" in this module stands for the "exponentially weighted moving + * average" of the number of cells sent on each circuit. The goal is to + * prioritize cells on circuits that have been quiet recently, by looking at + * those that have sent few cells over time, prioritizing recent times + * more than older ones. + * + * Specifically, a cell sent at time "now" has weight 1, but a time X ticks + * before now has weight ewma_scale_factor ^ X , where ewma_scale_factor is + * between 0.0 and 1.0. + * + * For efficiency, we do not re-scale these averages every time we send a + * cell: that would be horribly inefficient. Instead, we we keep the cell + * count on all circuits on the same circuitmux scaled relative to a single + * tick. When we add a new cell, we scale its weight depending on the time + * that has elapsed since the tick. We do re-scale the circuits on the + * circuitmux periodically, so that we don't overflow double. + * + * + * This module should be used through the interfaces in circuitmux.c, which it + * implements. + * **/ #define TOR_CIRCUITMUX_EWMA_C_ +#include "orconfig.h" + #include <math.h> #include "or.h" @@ -26,9 +50,10 @@ /*** Some useful constant #defines ***/ -/*DOCDOC*/ +/** Any halflife smaller than this number of seconds is considered to be + * "disabled". */ #define EPSILON 0.00001 -/*DOCDOC*/ +/** The natural logarithm of 0.5. */ #define LOG_ONEHALF -0.69314718055994529 /*** EWMA structures ***/ @@ -475,7 +500,7 @@ ewma_cmp_cmux(circuitmux_t *cmux_1, circuitmux_policy_data_t *pol_data_1, tor_assert(pol_data_2); p1 = TO_EWMA_POL_DATA(pol_data_1); - p2 = TO_EWMA_POL_DATA(pol_data_1); + p2 = TO_EWMA_POL_DATA(pol_data_2); if (p1 != p2) { /* Get the head cell_ewma_t from each queue */ diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c index f4db64ebca..418acc0024 100644 --- a/src/or/circuitstats.c +++ b/src/or/circuitstats.c @@ -9,6 +9,18 @@ * * \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. + * + * By keeping these statistics, a client learns when it should time out a slow + * circuit for being too slow, and when it should keep a circuit open in order + * to wait for it to complete. + * + * The information here is kept in a circuit_built_times_t structure, which is + * currently a singleton, but doesn't need to be. It's updated by calls to + * circuit_build_times_count_timeout() from circuituse.c, + * circuit_build_times_count_close() from circuituse.c, and + * circuit_build_times_add_time() from circuitbuild.c, and inspected by other + * calls into this module, mostly from circuitlist.c. Observations are + * persisted to disk via the or_state_t-related calls. */ #define CIRCUITSTATS_PRIVATE @@ -21,6 +33,8 @@ #include "control.h" #include "main.h" #include "networkstatus.h" +#include "rendclient.h" +#include "rendservice.h" #include "statefile.h" #undef log @@ -81,12 +95,14 @@ get_circuit_build_timeout_ms(void) /** * This function decides if CBT learning should be disabled. It returns - * true if one or more of the following four conditions are met: + * true if one or more of the following conditions are met: * * 1. If the cbtdisabled consensus parameter is set. * 2. If the torrc option LearnCircuitBuildTimeout is false. * 3. If we are a directory authority * 4. If we fail to write circuit build time history to our state file. + * 5. If we are compiled or configured in Tor2web mode + * 6. If we are configured in Single Onion mode */ int circuit_build_times_disabled(void) @@ -94,14 +110,30 @@ circuit_build_times_disabled(void) if (unit_tests) { return 0; } else { + const or_options_t *options = get_options(); int consensus_disabled = networkstatus_get_param(NULL, "cbtdisabled", 0, 0, 1); - int config_disabled = !get_options()->LearnCircuitBuildTimeout; - int dirauth_disabled = get_options()->AuthoritativeDir; + int config_disabled = !options->LearnCircuitBuildTimeout; + int dirauth_disabled = options->AuthoritativeDir; int state_disabled = did_last_state_file_write_fail() ? 1 : 0; + /* LearnCircuitBuildTimeout and Tor2web/Single Onion Services are + * incompatible in two ways: + * + * - LearnCircuitBuildTimeout results in a low CBT, which + * Single Onion use of one-hop intro and rendezvous circuits lowers + * much further, producing *far* too many timeouts. + * + * - The adaptive CBT code does not update its timeout estimate + * using build times for single-hop circuits. + * + * If we fix both of these issues someday, we should test + * these modes with LearnCircuitBuildTimeout on again. */ + int tor2web_disabled = rend_client_allow_non_anonymous_connection(options); + int single_onion_disabled = rend_service_allow_non_anonymous_connection( + options); if (consensus_disabled || config_disabled || dirauth_disabled || - state_disabled) { + state_disabled || tor2web_disabled || single_onion_disabled) { #if 0 log_debug(LD_CIRC, "CircuitBuildTime learning is disabled. " @@ -309,7 +341,6 @@ circuit_build_times_min_timeout(void) "circuit_build_times_min_timeout() called, cbtmintimeout is %d", num); } - return num; } @@ -469,7 +500,7 @@ circuit_build_times_get_initial_timeout(void) */ if (!unit_tests && get_options()->CircuitBuildTimeout) { timeout = get_options()->CircuitBuildTimeout*1000; - if (get_options()->LearnCircuitBuildTimeout && + if (!circuit_build_times_disabled() && timeout < circuit_build_times_min_timeout()) { log_warn(LD_CIRC, "Config CircuitBuildTimeout too low. Setting to %ds", circuit_build_times_min_timeout()/1000); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 44bf94cce5..1adb6fc887 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -6,7 +6,25 @@ /** * \file circuituse.c - * \brief Launch the right sort of circuits and attach streams to them. + * \brief Launch the right sort of circuits and attach the right streams to + * them. + * + * As distinct from circuitlist.c, which manages lookups to find circuits, and + * circuitbuild.c, which handles the logistics of circuit construction, this + * module keeps track of which streams can be attached to which circuits (in + * circuit_get_best()), and attaches streams to circuits (with + * circuit_try_attaching_streams(), connection_ap_handshake_attach_circuit(), + * and connection_ap_handshake_attach_chosen_circuit() ). + * + * This module also makes sure that we are building circuits for all of the + * predicted ports, using circuit_remove_handled_ports(), + * circuit_stream_is_being_handled(), and circuit_build_needed_cirs(). It + * handles launching circuits for specific targets using + * circuit_launch_by_extend_info(). + * + * This is also where we handle expiring circuits that have been around for + * too long without actually completing, along with the circuit_build_timeout + * logic in circuitstats.c. **/ #include "or.h" @@ -1859,16 +1877,22 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, c->state, conn_state_to_string(c->type, c->state)); } tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT); + + /* Will the exit policy of the exit node apply to this stream? */ check_exit_policy = conn->socks_request->command == SOCKS_COMMAND_CONNECT && !conn->use_begindir && !connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(conn)); + + /* Does this connection want a one-hop circuit? */ want_onehop = conn->want_onehop; + /* Do we need a high-uptime circuit? */ need_uptime = !conn->want_onehop && !conn->use_begindir && smartlist_contains_int_as_string(options->LongLivedPorts, conn->socks_request->port); + /* Do we need an "internal" circuit? */ if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) need_internal = 1; else if (conn->use_begindir || conn->want_onehop) @@ -1876,21 +1900,31 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, else need_internal = 0; - circ = circuit_get_best(conn, 1, desired_circuit_purpose, + /* We now know what kind of circuit we need. See if there is an + * open circuit that we can use for this stream */ + circ = circuit_get_best(conn, 1 /* Insist on open circuits */, + desired_circuit_purpose, need_uptime, need_internal); if (circ) { + /* We got a circuit that will work for this stream! We can return it. */ *circp = circ; return 1; /* we're happy */ } + /* Okay, there's no circuit open that will work for this stream. Let's + * see if there's an in-progress circuit or if we have to launch one */ + + /* Do we know enough directory info to build circuits at all? */ int have_path = have_enough_path_info(!need_internal); if (!want_onehop && (!router_have_minimum_dir_info() || !have_path)) { + /* If we don't have enough directory information, we can't build + * multihop circuits. + */ if (!connection_get_by_type(CONN_TYPE_DIR)) { int severity = LOG_NOTICE; - /* FFFF if this is a tunneled directory fetch, don't yell - * as loudly. the user doesn't even know it's happening. */ + /* Retry some stuff that might help the connection work. */ if (entry_list_is_constrained(options) && entries_known_but_down(options)) { log_fn(severity, LD_APP|LD_DIR, @@ -1911,14 +1945,16 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, routerlist_retry_directory_downloads(time(NULL)); } } - /* the stream will be dealt with when router_have_minimum_dir_info becomes - * 1, or when all directory attempts fail and directory_all_unreachable() + /* Since we didn't have enough directory info, we can't attach now. The + * stream will be dealt with when router_have_minimum_dir_info becomes 1, + * or when all directory attempts fail and directory_all_unreachable() * kills it. */ return 0; } - /* Do we need to check exit policy? */ + /* Check whether the exit policy of the chosen exit, or the exit policies + * of _all_ nodes, would forbid this node. */ if (check_exit_policy) { if (!conn->chosen_exit_name) { struct in_addr in; @@ -1959,16 +1995,25 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } } - /* is one already on the way? */ - circ = circuit_get_best(conn, 0, desired_circuit_purpose, + /* Now, check whether there already a circuit on the way that could handle + * this stream. This check matches the one above, but this time we + * do not require that the circuit will work. */ + circ = circuit_get_best(conn, 0 /* don't insist on open circuits */, + desired_circuit_purpose, need_uptime, need_internal); if (circ) log_debug(LD_CIRC, "one on the way!"); + if (!circ) { + /* No open or in-progress circuit could handle this stream! We + * will have to launch one! + */ + + /* THe chosen exit node, if there is one. */ extend_info_t *extend_info=NULL; - uint8_t new_circ_purpose; const int n_pending = count_pending_general_client_circuits(); + /* Do we have too many pending circuits? */ if (n_pending >= options->MaxClientCircuitsPending) { static ratelim_t delay_limit = RATELIM_INIT(10*60); char *m; @@ -1982,6 +2027,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, return 0; } + /* If this is a hidden service trying to start an introduction point, + * handle that case. */ if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) { /* need to pick an intro point */ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data; @@ -2019,7 +2066,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, "Discarding this circuit.", conn->chosen_exit_name); return -1; } - } else { + } else { /* ! (r && node_has_descriptor(r)) */ log_debug(LD_DIR, "considering %d, %s", want_onehop, conn->chosen_exit_name); if (want_onehop && conn->chosen_exit_name[0] == '$') { @@ -2042,7 +2089,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, extend_info = extend_info_new(conn->chosen_exit_name+1, digest, NULL, NULL, &addr, conn->socks_request->port); - } else { + } else { /* ! (want_onehop && conn->chosen_exit_name[0] == '$') */ /* We will need an onion key for the router, and we * don't have one. Refuse or relax requirements. */ log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, @@ -2060,8 +2107,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } } } - } + } /* Done checking for general circutis with chosen exits. */ + /* What purpose do we need to launch this circuit with? */ + uint8_t new_circ_purpose; if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED) new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND; else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) @@ -2070,6 +2119,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, new_circ_purpose = desired_circuit_purpose; #ifdef ENABLE_TOR2WEB_MODE + /* If tor2Web is on, then hidden service requests should be one-hop. + */ if (options->Tor2webMode && (new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND || new_circ_purpose == CIRCUIT_PURPOSE_C_INTRODUCING)) { @@ -2077,6 +2128,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } #endif + /* Determine what kind of a circuit to launch, and actually launch it. */ { int flags = CIRCLAUNCH_NEED_CAPACITY; if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL; @@ -2088,6 +2140,8 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, extend_info_free(extend_info); + /* Now trigger things that need to happen when we launch circuits */ + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) { /* We just caused a circuit to get built because of this stream. * If this stream has caused a _lot_ of circuits to be built, that's @@ -2111,6 +2165,10 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } } } /* endif (!circ) */ + + /* We either found a good circuit, or launched a new circuit, or failed to + * do so. Report success, and delay. */ + if (circ) { /* Mark the circuit with the isolation fields for this connection. * When the circuit arrives, we'll clear these flags: this is @@ -2310,7 +2368,9 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, pathbias_count_use_attempt(circ); + /* Now, actually link the connection. */ link_apconn_to_circ(conn, circ, cpath); + tor_assert(conn->socks_request); if (conn->socks_request->command == SOCKS_COMMAND_CONNECT) { if (!conn->use_begindir) @@ -2325,12 +2385,11 @@ connection_ap_handshake_attach_chosen_circuit(entry_connection_t *conn, return 1; } -/** Try to find a safe live circuit for CONN_TYPE_AP connection conn. If - * we don't find one: if conn cannot be handled by any known nodes, - * warn and return -1 (conn needs to die, and is maybe already marked); - * else launch new circuit (if necessary) and return 0. - * Otherwise, associate conn with a safe live circuit, do the - * right next step, and return 1. +/** Try to find a safe live circuit for stream <b>conn</b>. If we find one, + * attach the stream, send appropriate cells, and return 1. Otherwise, + * try to launch new circuit(s) for the stream. If we can launch + * circuits, return 0. Otherwise, if we simply can't proceed with + * this stream, return -1. (conn needs to die, and is maybe already marked). */ /* XXXX this function should mark for close whenever it returns -1; * its callers shouldn't have to worry about that. */ @@ -2349,6 +2408,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) conn_age = (int)(time(NULL) - base_conn->timestamp_created); + /* Is this connection so old that we should give up on it? */ if (conn_age >= get_options()->SocksTimeout) { int severity = (tor_addr_is_null(&base_conn->addr) && !base_conn->port) ? LOG_INFO : LOG_NOTICE; @@ -2359,12 +2419,14 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) return -1; } + /* We handle "general" (non-onion) connections much more straightforwardly. + */ if (!connection_edge_is_rendezvous_stream(ENTRY_TO_EDGE_CONN(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. */ + * We check here because the 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) { @@ -2382,6 +2444,9 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) } } + /* If we have a chosen exit, we need to use a circuit that's + * open to that exit. See what exit we meant, and whether we can use it. + */ if (conn->chosen_exit_name) { const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); int opt = conn->chosen_exit_optional; @@ -2395,6 +2460,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) "Requested exit point '%s' is not known. %s.", conn->chosen_exit_name, opt ? "Trying others" : "Closing"); if (opt) { + /* If we are allowed to ignore the .exit request, do so */ conn->chosen_exit_optional = 0; tor_free(conn->chosen_exit_name); return 0; @@ -2407,6 +2473,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) "would refuse request. %s.", conn->chosen_exit_name, opt ? "Trying others" : "Closing"); if (opt) { + /* If we are allowed to ignore the .exit request, do so */ conn->chosen_exit_optional = 0; tor_free(conn->chosen_exit_name); return 0; @@ -2415,11 +2482,15 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) } } - /* find the circuit that we should use, if there is one. */ + /* Find the circuit that we should use, if there is one. Otherwise + * launch it. */ retval = circuit_get_open_circ_or_launch( conn, CIRCUIT_PURPOSE_C_GENERAL, &circ); - if (retval < 1) // XXXX++ if we totally fail, this still returns 0 -RD + if (retval < 1) { + /* We were either told "-1" (complete failure) or 0 (circuit in + * progress); we can't attach this stream yet. */ return retval; + } log_debug(LD_APP|LD_CIRC, "Attaching apconn to circ %u (stream %d sec old).", @@ -2428,7 +2499,8 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) * sucking. */ circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ); - /* We have found a suitable circuit for our conn. Hurray. */ + /* We have found a suitable circuit for our conn. Hurray. Do + * the attachment. */ return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL); } else { /* we're a rendezvous conn */ diff --git a/src/or/command.c b/src/or/command.c index 5ad92bed1e..5866c386e4 100644 --- a/src/or/command.c +++ b/src/or/command.c @@ -7,6 +7,26 @@ /** * \file command.c * \brief Functions for processing incoming cells. + * + * When we receive a cell from a client or a relay, it arrives on some + * channel, and tells us what to do with it. In this module, we dispatch based + * on the cell type using the functions command_process_cell() and + * command_process_var_cell(), and deal with the cell accordingly. (These + * handlers are installed on a channel with the command_setup_channel() + * function.) + * + * Channels have a chance to handle some cell types on their own before they + * are ever passed here --- typically, they do this for cells that are + * specific to a given channel type. For example, in channeltls.c, the cells + * for the initial connection handshake are handled before we get here. (Of + * course, the fact that there _is_ only one channel type for now means that + * we may have gotten the factoring wrong here.) + * + * Handling other cell types is mainly farmed off to other modules, after + * initial sanity-checking. CREATE* cells are handled ultimately in onion.c, + * CREATED* cells trigger circuit creation in circuitbuild.c, DESTROY cells + * are handled here (since they're simple), and RELAY cells, in all their + * complexity, are passed off to relay.c. **/ /* In-points to command.c: diff --git a/src/or/config.c b/src/or/config.c index 31bf81877d..fef12083e0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -6,7 +6,56 @@ /** * \file config.c - * \brief Code to parse and interpret configuration files. + * \brief Code to interpret the user's configuration of Tor. + * + * This module handles torrc configuration file, including parsing it, + * combining it with torrc.defaults and the command line, allowing + * user changes to it (via editing and SIGHUP or via the control port), + * writing it back to disk (because of SAVECONF from the control port), + * and -- most importantly, acting on it. + * + * The module additionally has some tools for manipulating and + * inspecting values that are calculated as a result of the + * configured options. + * + * <h3>How to add new options</h3> + * + * To add new items to the torrc, there are a minimum of three places to edit: + * <ul> + * <li>The or_options_t structure in or.h, where the options are stored. + * <li>The option_vars_ array below in this module, which configures + * the names of the torrc options, their types, their multiplicities, + * and their mappings to fields in or_options_t. + * <li>The manual in doc/tor.1.txt, to document what the new option + * is, and how it works. + * </ul> + * + * Additionally, you might need to edit these places too: + * <ul> + * <li>options_validate() below, in case you want to reject some possible + * values of the new configuration option. + * <li>options_transition_allowed() below, in case you need to + * forbid some or all changes in the option while Tor is + * running. + * <li>options_transition_affects_workers(), in case changes in the option + * might require Tor to relaunch or reconfigure its worker threads. + * <li>options_transition_affects_descriptor(), in case changes in the + * option might require a Tor relay to build and publish a new server + * descriptor. + * <li>options_act() and/or options_act_reversible(), in case there's some + * action that needs to be taken immediately based on the option's + * value. + * </ul> + * + * <h3>Changing the value of an option</h3> + * + * Because of the SAVECONF command from the control port, it's a bad + * idea to change the value of any user-configured option in the + * or_options_t. If you want to sometimes do this anyway, we recommend + * that you create a secondary field in or_options_t; that you have the + * user option linked only to the secondary field; that you use the + * secondary field to initialize the one that Tor actually looks at; and that + * you use the one Tor looks as the one that you modify. **/ #define CONFIG_PRIVATE @@ -18,6 +67,7 @@ #include "circuitlist.h" #include "circuitmux.h" #include "circuitmux_ewma.h" +#include "circuitstats.h" #include "config.h" #include "connection.h" #include "connection_edge.h" @@ -67,6 +117,9 @@ /* Prefix used to indicate a Unix socket in a FooPort configuration. */ static const char unix_socket_prefix[] = "unix:"; +/* Prefix used to indicate a Unix socket with spaces in it, in a FooPort + * configuration. */ +static const char unix_q_socket_prefix[] = "unix:\""; /** A list of abbreviations and aliases to map command-line options, obsolete * option names, or alternative option names, to their current values. */ @@ -297,6 +350,8 @@ static config_var_t option_vars_[] = { V(HidServAuth, LINELIST, NULL), V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"), V(CloseHSServiceRendCircuitsImmediatelyOnTimeout, BOOL, "0"), + V(HiddenServiceSingleHopMode, BOOL, "0"), + V(HiddenServiceNonAnonymousMode,BOOL, "0"), V(HTTPProxy, STRING, NULL), V(HTTPProxyAuthenticator, STRING, NULL), V(HTTPSProxy, STRING, NULL), @@ -434,11 +489,11 @@ static config_var_t option_vars_[] = { OBSOLETE("TunnelDirConns"), V(UpdateBridgesFromAuthority, BOOL, "0"), V(UseBridges, BOOL, "0"), - V(UseEntryGuards, BOOL, "1"), + VAR("UseEntryGuards", BOOL, UseEntryGuards_option, "1"), V(UseEntryGuardsAsDirGuards, BOOL, "1"), V(UseGuardFraction, AUTOBOOL, "auto"), V(UseMicrodescriptors, AUTOBOOL, "auto"), - V(UseNTorHandshake, AUTOBOOL, "1"), + OBSOLETE("UseNTorHandshake"), V(User, STRING, NULL), OBSOLETE("UserspaceIOCPBuffers"), V(AuthDirSharedRandomness, BOOL, "1"), @@ -613,7 +668,6 @@ static const config_deprecation_t option_deprecation_notes_[] = { "to accidentally lose your anonymity by leaking DNS information" }, { "TLSECGroup", "The default is a nice secure choice; the other option " "is less secure." }, - { "UseNTorHandshake", "The ntor handshake should always be used." }, { "ControlListenAddress", "Use ControlPort instead." }, { "DirListenAddress", "Use DirPort instead, possibly with the " "NoAdvertise sub-option" }, @@ -776,7 +830,7 @@ set_options(or_options_t *new_val, char **msg) tor_free(line); } } else { - smartlist_add(elements, tor_strdup(options_format.vars[i].name)); + smartlist_add_strdup(elements, options_format.vars[i].name); smartlist_add(elements, NULL); } } @@ -1559,10 +1613,10 @@ options_act(const or_options_t *old_options) if (consider_adding_dir_servers(options, old_options) < 0) return -1; -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif + if (rend_non_anonymous_mode_enabled(options)) { + log_warn(LD_GENERAL, "This copy of Tor was compiled or configured to run " + "in a non-anonymous mode. It will provide NO ANONYMITY."); + } #ifdef ENABLE_TOR2WEB_MODE /* LCOV_EXCL_START */ @@ -1724,8 +1778,27 @@ options_act(const or_options_t *old_options) monitor_owning_controller_process(options->OwningControllerProcess); + /* We must create new keys after we poison the directories, because our + * poisoning code checks for existing keys, and refuses to modify their + * directories. */ + + /* If we use non-anonymous single onion services, make sure we poison any + new hidden service directories, so that we never accidentally launch the + non-anonymous hidden services thinking they are anonymous. */ + if (running_tor && rend_service_non_anonymous_mode_enabled(options)) { + if (options->RendConfigLines && !num_rend_services()) { + log_warn(LD_BUG,"Error: hidden services configured, but not parsed."); + return -1; + } + if (rend_service_poison_new_single_onion_dirs(NULL) < 0) { + log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous" + "."); + return -1; + } + } + /* reload keys as needed for rendezvous services. */ - if (rend_service_load_all_keys()<0) { + if (rend_service_load_all_keys(NULL)<0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); return -1; } @@ -2291,6 +2364,14 @@ reset_last_resolved_addr(void) last_resolved_addr = 0; } +/* Return true if <b>options</b> is using the default authorities, and false + * if any authority-related option has been overridden. */ +int +using_default_dir_authorities(const or_options_t *options) +{ + return (!options->DirAuthorities && !options->AlternateDirAuthority); +} + /** * Attempt getting our non-local (as judged by tor_addr_is_internal() * function) IP address using following techniques, listed in @@ -2450,7 +2531,7 @@ resolve_my_address(int warn_severity, const or_options_t *options, addr_string = tor_dup_ip(addr); if (tor_addr_is_internal(&myaddr, 0)) { /* make sure we're ok with publishing an internal IP */ - if (!options->DirAuthorities && !options->AlternateDirAuthority) { + if (using_default_dir_authorities(options)) { /* if they are using the default authorities, disallow internal IPs * always. */ log_fn(warn_severity, LD_CONFIG, @@ -2797,6 +2878,86 @@ warn_about_relative_paths(or_options_t *options) } } +/* Validate options related to single onion services. + * Modifies some options that are incompatible with single onion services. + * On failure returns -1, and sets *msg to an error string. + * Returns 0 on success. */ +STATIC int +options_validate_single_onion(or_options_t *options, char **msg) +{ + /* The two single onion service options must have matching values. */ + if (options->HiddenServiceSingleHopMode && + !options->HiddenServiceNonAnonymousMode) { + REJECT("HiddenServiceSingleHopMode does not provide any server anonymity. " + "It must be used with HiddenServiceNonAnonymousMode set to 1."); + } + if (options->HiddenServiceNonAnonymousMode && + !options->HiddenServiceSingleHopMode) { + REJECT("HiddenServiceNonAnonymousMode does not provide any server " + "anonymity. It must be used with HiddenServiceSingleHopMode set to " + "1."); + } + + /* Now that we've checked that the two options are consistent, we can safely + * call the rend_service_* functions that abstract these options. */ + + /* If you run an anonymous client with an active Single Onion service, the + * client loses anonymity. */ + const int client_port_set = (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->DNSPort_set); + if (rend_service_non_anonymous_mode_enabled(options) && client_port_set && + !options->Tor2webMode) { + REJECT("HiddenServiceNonAnonymousMode is incompatible with using Tor as " + "an anonymous client. Please set Socks/Trans/NATD/DNSPort to 0, or " + "HiddenServiceNonAnonymousMode to 0, or use the non-anonymous " + "Tor2webMode."); + } + + /* If you run a hidden service in non-anonymous mode, the hidden service + * loses anonymity, even if SOCKSPort / Tor2web mode isn't used. */ + if (!rend_service_non_anonymous_mode_enabled(options) && + options->RendConfigLines && options->Tor2webMode) { + REJECT("Non-anonymous (Tor2web) mode is incompatible with using Tor as a " + "hidden service. Please remove all HiddenServiceDir lines, or use " + "a version of tor compiled without --enable-tor2web-mode, or use " + " HiddenServiceNonAnonymousMode."); + } + + if (rend_service_allow_non_anonymous_connection(options) + && options->UseEntryGuards) { + /* Single Onion services only use entry guards when uploading descriptors, + * all other connections are one-hop. Further, Single Onions causes the + * hidden service code to do things which break the path bias + * detector, and it's far easier to turn off entry guards (and + * thus the path bias detector with it) than to figure out how to + * make path bias compatible with single onions. + */ + log_notice(LD_CONFIG, + "HiddenServiceSingleHopMode is enabled; disabling " + "UseEntryGuards."); + options->UseEntryGuards = 0; + } + + /* Check if existing hidden service keys were created in a different + * single onion service mode, and refuse to launch if they + * have. We'll poison new keys in options_act() just before we create them. + */ + if (rend_service_list_verify_single_onion_poison(NULL, options) < 0) { + log_warn(LD_GENERAL, "We are configured with " + "HiddenServiceNonAnonymousMode %d, but one or more hidden " + "service keys were created in %s mode. This is not allowed.", + rend_service_non_anonymous_mode_enabled(options) ? 1 : 0, + rend_service_non_anonymous_mode_enabled(options) ? + "an anonymous" : "a non-anonymous" + ); + return -1; + } + + return 0; +} + /** 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 @@ -2823,6 +2984,12 @@ options_validate(or_options_t *old_options, or_options_t *options, tor_assert(msg); *msg = NULL; + /* Set UseEntryGuards from the configured value, before we check it below. + * We change UseEntryGuards whenn it's incompatible with other options, + * but leave UseEntryGuards_option with the original value. + * Always use the value of UseEntryGuards, not UseEntryGuards_option. */ + options->UseEntryGuards = options->UseEntryGuards_option; + warn_about_relative_paths(options); if (server_mode(options) && @@ -3198,10 +3365,6 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->UseBridges && options->EntryNodes) REJECT("You cannot set both UseBridges and EntryNodes."); - if (options->EntryNodes && !options->UseEntryGuards) { - REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); - } - options->MaxMemInQueues = compute_real_max_mem_in_queues(options->MaxMemInQueues_raw, server_mode(options)); @@ -3292,25 +3455,11 @@ options_validate(or_options_t *old_options, or_options_t *options, options->PredictedPortsRelevanceTime = MAX_PREDICTED_CIRCS_RELEVANCE; } -#ifdef ENABLE_TOR2WEB_MODE - if (options->Tor2webMode && options->LearnCircuitBuildTimeout) { - /* LearnCircuitBuildTimeout and Tor2webMode are incompatible in - * two ways: - * - * - LearnCircuitBuildTimeout results in a low CBT, which - * Tor2webMode's use of one-hop rendezvous circuits lowers - * much further, producing *far* too many timeouts. - * - * - The adaptive CBT code does not update its timeout estimate - * using build times for single-hop circuits. - * - * If we fix both of these issues someday, we should test - * Tor2webMode with LearnCircuitBuildTimeout on again. */ - log_notice(LD_CONFIG,"Tor2webMode is enabled; turning " - "LearnCircuitBuildTimeout off."); - options->LearnCircuitBuildTimeout = 0; - } + /* Check the Single Onion Service options */ + if (options_validate_single_onion(options, msg) < 0) + return -1; +#ifdef ENABLE_TOR2WEB_MODE if (options->Tor2webMode && options->UseEntryGuards) { /* tor2web mode clients do not (and should not) use entry guards * in any meaningful way. Further, tor2web mode causes the hidden @@ -3330,8 +3479,13 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("Tor2webRendezvousPoints cannot be set without Tor2webMode."); } + if (options->EntryNodes && !options->UseEntryGuards) { + REJECT("If EntryNodes is set, UseEntryGuards must be enabled."); + } + if (!(options->UseEntryGuards) && - (options->RendConfigLines != NULL)) { + (options->RendConfigLines != NULL) && + !rend_service_allow_non_anonymous_connection(options)) { log_warn(LD_CONFIG, "UseEntryGuards is disabled, but you have configured one or more " "hidden services on this Tor instance. Your hidden services " @@ -3354,6 +3508,17 @@ options_validate(or_options_t *old_options, or_options_t *options, return -1; } + /* Single Onion Services: non-anonymous hidden services */ + if (rend_service_non_anonymous_mode_enabled(options)) { + log_warn(LD_CONFIG, + "HiddenServiceNonAnonymousMode is set. Every hidden service on " + "this tor instance is NON-ANONYMOUS. If " + "the HiddenServiceNonAnonymousMode option is changed, Tor will " + "refuse to launch hidden services from the same directories, to " + "protect your anonymity against config errors. This setting is " + "for experimental use only."); + } + if (!options->LearnCircuitBuildTimeout && options->CircuitBuildTimeout && options->CircuitBuildTimeout < RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT) { log_warn(LD_CONFIG, @@ -3365,8 +3530,15 @@ options_validate(or_options_t *old_options, or_options_t *options, RECOMMENDED_MIN_CIRCUIT_BUILD_TIMEOUT ); } else if (!options->LearnCircuitBuildTimeout && !options->CircuitBuildTimeout) { - log_notice(LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but didn't " - "a CircuitBuildTimeout. I'll pick a plausible default."); + int severity = LOG_NOTICE; + /* Be a little quieter if we've deliberately disabled + * LearnCircuitBuildTimeout. */ + if (circuit_build_times_disabled()) { + severity = LOG_INFO; + } + log_fn(severity, LD_CONFIG, "You disabled LearnCircuitBuildTimeout, but " + "didn't specify a CircuitBuildTimeout. I'll pick a plausible " + "default."); } if (options->PathBiasNoticeRate > 1.0) { @@ -4296,6 +4468,19 @@ options_transition_allowed(const or_options_t *old, return -1; } + if (old->HiddenServiceSingleHopMode != new_val->HiddenServiceSingleHopMode) { + *msg = tor_strdup("While Tor is running, changing " + "HiddenServiceSingleHopMode is not allowed."); + return -1; + } + + if (old->HiddenServiceNonAnonymousMode != + new_val->HiddenServiceNonAnonymousMode) { + *msg = tor_strdup("While Tor is running, changing " + "HiddenServiceNonAnonymousMode is not allowed."); + return -1; + } + if (old->DisableDebuggerAttachment && !new_val->DisableDebuggerAttachment) { *msg = tor_strdup("While Tor is running, disabling " @@ -5199,7 +5384,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); if (smartlist_len(elts) == 0) - smartlist_add(elts, tor_strdup("stdout")); + smartlist_add_strdup(elts, "stdout"); if (smartlist_len(elts) == 1 && (!strcasecmp(smartlist_get(elts,0), "stdout") || @@ -5326,10 +5511,14 @@ bridge_line_free(bridge_line_t *bridge_line) tor_free(bridge_line); } -/** Read the contents of a Bridge line from <b>line</b>. Return 0 - * if the line is well-formed, and -1 if it isn't. If - * <b>validate_only</b> is 0, and the line is well-formed, then add - * the bridge described in the line to our internal bridge list. +/** Parse the contents of a string, <b>line</b>, containing a Bridge line, + * into a bridge_line_t. + * + * Validates that the IP:PORT, fingerprint, and SOCKS arguments (given to the + * Pluggable Transport, if a one was specified) are well-formed. + * + * Returns NULL If the Bridge line could not be validated, and returns a + * bridge_line_t containing the parsed information otherwise. * * Bridge line format: * Bridge [transport] IP:PORT [id-fingerprint] [k=v] [k=v] ... @@ -5730,7 +5919,7 @@ get_options_from_transport_options_line(const char *line,const char *transport) } /* add it to the options smartlist */ - smartlist_add(options, tor_strdup(option)); + smartlist_add_strdup(options, option); log_debug(LD_CONFIG, "Added %s to the list of options", escaped(option)); } SMARTLIST_FOREACH_END(option); @@ -6055,6 +6244,8 @@ port_cfg_new(size_t namelen) tor_assert(namelen <= SIZE_T_CEILING - sizeof(port_cfg_t) - 1); port_cfg_t *cfg = tor_malloc_zero(sizeof(port_cfg_t) + namelen + 1); cfg->entry_cfg.ipv4_traffic = 1; + cfg->entry_cfg.dns_request = 1; + cfg->entry_cfg.onion_traffic = 1; cfg->entry_cfg.cache_ipv4_answers = 1; cfg->entry_cfg.prefer_ipv6_virtaddr = 1; return cfg; @@ -6157,54 +6348,61 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal) } SMARTLIST_FOREACH_END(port); } -#ifdef HAVE_SYS_UN_H - -/** Parse the given <b>addrport</b> and set <b>path_out</b> if a Unix socket - * path is found. Return 0 on success. On error, a negative value is - * returned, -ENOENT if no Unix statement found, -EINVAL if the socket path - * is empty and -ENOSYS if AF_UNIX is not supported (see function in the - * #else statement below). */ - -int -config_parse_unix_port(const char *addrport, char **path_out) -{ - tor_assert(path_out); - tor_assert(addrport); - - if (strcmpstart(addrport, unix_socket_prefix)) { - /* Not a Unix socket path. */ - return -ENOENT; - } - - if (strlen(addrport + strlen(unix_socket_prefix)) == 0) { - /* Empty socket path, not very usable. */ - return -EINVAL; - } - - *path_out = tor_strdup(addrport + strlen(unix_socket_prefix)); - return 0; -} - -#else /* defined(HAVE_SYS_UN_H) */ - +/** + * Take a string (<b>line</b>) that begins with either an address:port, a + * port, or an AF_UNIX address, optionally quoted, prefixed with + * "unix:". Parse that line, and on success, set <b>addrport_out</b> to a new + * string containing the beginning portion (without prefix). Iff there was a + * unix: prefix, set <b>is_unix_out</b> to true. On success, also set + * <b>rest_out</b> to point to the part of the line after the address portion. + * + * Return 0 on success, -1 on failure. + */ int -config_parse_unix_port(const char *addrport, char **path_out) +port_cfg_line_extract_addrport(const char *line, + char **addrport_out, + int *is_unix_out, + const char **rest_out) { - tor_assert(path_out); - tor_assert(addrport); + tor_assert(line); + tor_assert(addrport_out); + tor_assert(is_unix_out); + tor_assert(rest_out); + + line = eat_whitespace(line); + + if (!strcmpstart(line, unix_q_socket_prefix)) { + // It starts with unix:" + size_t sz; + *is_unix_out = 1; + *addrport_out = NULL; + line += strlen(unix_socket_prefix); /*No q: Keep the quote */ + *rest_out = unescape_string(line, addrport_out, &sz); + if (!*rest_out || (*addrport_out && sz != strlen(*addrport_out))) { + tor_free(*addrport_out); + return -1; + } + *rest_out = eat_whitespace(*rest_out); + return 0; + } else { + // Is there a unix: prefix? + if (!strcmpstart(line, unix_socket_prefix)) { + line += strlen(unix_socket_prefix); + *is_unix_out = 1; + } else { + *is_unix_out = 0; + } - if (strcmpstart(addrport, unix_socket_prefix)) { - /* Not a Unix socket path. */ - return -ENOENT; + const char *end = find_whitespace(line); + if (BUG(!end)) { + end = strchr(line, '\0'); // LCOV_EXCL_LINE -- this can't be NULL + } + tor_assert(end && end >= line); + *addrport_out = tor_strndup(line, end - line); + *rest_out = eat_whitespace(end); + return 0; } - - log_warn(LD_CONFIG, - "Port configuration %s is for an AF_UNIX socket, but we have no" - "support available on this platform", - escaped(addrport)); - return -ENOSYS; } -#endif /* defined(HAVE_SYS_UN_H) */ static void warn_client_dns_cache(const char *option, int disabling) @@ -6325,8 +6523,7 @@ parse_port_config(smartlist_t *out, tor_addr_make_unspec(&cfg->addr); /* Server ports default to 0.0.0.0 */ cfg->server_cfg.no_listen = 1; cfg->server_cfg.bind_ipv4_only = 1; - cfg->entry_cfg.ipv4_traffic = 1; - cfg->entry_cfg.prefer_ipv6_virtaddr = 1; + /* cfg->entry_cfg defaults are already set by port_cfg_new */ smartlist_add(out, cfg); } @@ -6385,44 +6582,54 @@ parse_port_config(smartlist_t *out, /* At last we can actually parse the FooPort lines. The syntax is: * [Addr:](Port|auto) [Options].*/ elts = smartlist_new(); + char *addrport = NULL; for (; ports; ports = ports->next) { tor_addr_t addr; - int port, ret; + int port; 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; int ok; + /* This must be kept in sync with port_cfg_new's defaults */ int no_listen = 0, no_advertise = 0, all_addrs = 0, bind_ipv4_only = 0, bind_ipv6_only = 0, - ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, + ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, dns_request = 1, + onion_traffic = 1, cache_ipv4 = 1, use_cached_ipv4 = 0, cache_ipv6 = 0, use_cached_ipv6 = 0, 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); - if (smartlist_len(elts) == 0) { - log_warn(LD_CONFIG, "Invalid %sPort line with no value", portname); + int is_unix_tagged_addr = 0; + const char *rest_of_line = NULL; + if (port_cfg_line_extract_addrport(ports->value, + &addrport, &is_unix_tagged_addr, &rest_of_line)<0) { + log_warn(LD_CONFIG, "Invalid %sPort line with unparsable address", + portname); + goto err; + } + if (strlen(addrport) == 0) { + log_warn(LD_CONFIG, "Invalid %sPort line with no address", portname); goto err; } - /* Now parse the addr/port value */ - addrport = smartlist_get(elts, 0); + /* Split the remainder... */ + smartlist_split_string(elts, rest_of_line, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); /* Let's start to check if it's a Unix socket path. */ - ret = config_parse_unix_port(addrport, &unix_socket_path); - if (ret < 0 && ret != -ENOENT) { - if (ret == -EINVAL) { - log_warn(LD_CONFIG, "Empty Unix socket path."); - } + if (is_unix_tagged_addr) { +#ifndef HAVE_SYS_UN_H + log_warn(LD_CONFIG, "Unix sockets not supported on this system."); goto err; +#endif + unix_socket_path = addrport; + addrport = NULL; } if (unix_socket_path && @@ -6434,6 +6641,8 @@ parse_port_config(smartlist_t *out, if (unix_socket_path) { port = 1; } else if (is_unix_socket) { + if (BUG(!addrport)) + goto err; // LCOV_EXCL_LINE unreachable, but coverity can't tell that unix_socket_path = tor_strdup(addrport); if (!strcmp(addrport, "0")) port = 0; @@ -6480,9 +6689,6 @@ parse_port_config(smartlist_t *out, if (use_server_options) { /* This is a server port; parse advertising options */ SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { - if (elt_sl_idx == 0) - continue; /* Skip addr:port */ - if (!strcasecmp(elt, "NoAdvertise")) { no_advertise = 1; } else if (!strcasecmp(elt, "NoListen")) { @@ -6530,8 +6736,6 @@ parse_port_config(smartlist_t *out, SMARTLIST_FOREACH_BEGIN(elts, char *, elt) { int no = 0, isoflag = 0; 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="), @@ -6585,6 +6789,24 @@ parse_port_config(smartlist_t *out, } else if (!strcasecmp(elt, "PreferIPv6")) { prefer_ipv6 = ! no; continue; + } else if (!strcasecmp(elt, "DNSRequest")) { + dns_request = ! no; + continue; + } else if (!strcasecmp(elt, "OnionTraffic")) { + onion_traffic = ! no; + continue; + } else if (!strcasecmp(elt, "OnionTrafficOnly")) { + /* Only connect to .onion addresses. Equivalent to + * NoDNSRequest, NoIPv4Traffic, NoIPv6Traffic. The option + * NoOnionTrafficOnly is not supported, it's too confusing. */ + if (no) { + log_warn(LD_CONFIG, "Unsupported %sPort option 'No%s'. Use " + "DNSRequest, IPv4Traffic, and/or IPv6Traffic instead.", + portname, escaped(elt)); + } else { + ipv4_traffic = ipv6_traffic = dns_request = 0; + } + continue; } } if (!strcasecmp(elt, "CacheIPv4DNS")) { @@ -6653,9 +6875,24 @@ parse_port_config(smartlist_t *out, else got_zero_port = 1; - if (ipv4_traffic == 0 && ipv6_traffic == 0) { - log_warn(LD_CONFIG, "You have a %sPort entry with both IPv4 and " - "IPv6 disabled; that won't work.", portname); + if (dns_request == 0 && listener_type == CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with DNS disabled; that " + "won't work.", portname); + goto err; + } + + if (ipv4_traffic == 0 && ipv6_traffic == 0 && onion_traffic == 0 + && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with all of IPv4 and " + "IPv6 and .onion disabled; that won't work.", portname); + goto err; + } + + if (dns_request == 1 && ipv4_traffic == 0 && ipv6_traffic == 0 + && listener_type != CONN_TYPE_AP_DNS_LISTENER) { + log_warn(LD_CONFIG, "You have a %sPort entry with DNSRequest enabled, " + "but IPv4 and IPv6 disabled; DNS-based sites won't work.", + portname); goto err; } @@ -6673,6 +6910,13 @@ parse_port_config(smartlist_t *out, goto err; } + if (unix_socket_path && (isolation & ISO_CLIENTADDR)) { + /* `IsolateClientAddr` is nonsensical in the context of AF_LOCAL. + * just silently remove the isolation flag. + */ + isolation &= ~ISO_CLIENTADDR; + } + if (out && port) { size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; port_cfg_t *cfg = port_cfg_new(namelen); @@ -6699,6 +6943,8 @@ parse_port_config(smartlist_t *out, cfg->entry_cfg.ipv4_traffic = ipv4_traffic; cfg->entry_cfg.ipv6_traffic = ipv6_traffic; cfg->entry_cfg.prefer_ipv6 = prefer_ipv6; + cfg->entry_cfg.dns_request = dns_request; + cfg->entry_cfg.onion_traffic = onion_traffic; cfg->entry_cfg.cache_ipv4_answers = cache_ipv4; cfg->entry_cfg.cache_ipv6_answers = cache_ipv6; cfg->entry_cfg.use_cached_ipv4_answers = use_cached_ipv4; @@ -6713,6 +6959,7 @@ parse_port_config(smartlist_t *out, } SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); + tor_free(addrport); } if (warn_nonlocal && out) { @@ -6736,18 +6983,22 @@ parse_port_config(smartlist_t *out, SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_free(elts); tor_free(unix_socket_path); + tor_free(addrport); return retval; } /** Return the number of ports which are actually going to listen with type - * <b>listenertype</b>. Do not count no_listen ports. Do not count unix - * sockets. */ + * <b>listenertype</b>. Do not count no_listen ports. Only count unix + * sockets if count_sockets is true. */ static int -count_real_listeners(const smartlist_t *ports, int listenertype) +count_real_listeners(const smartlist_t *ports, int listenertype, + int count_sockets) { int n = 0; SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { - if (port->server_cfg.no_listen || port->is_unix_addr) + if (port->server_cfg.no_listen) + continue; + if (!count_sockets && port->is_unix_addr) continue; if (port->type != listenertype) continue; @@ -6756,9 +7007,8 @@ count_real_listeners(const smartlist_t *ports, int listenertype) return n; } -/** Parse all client port types (Socks, DNS, Trans, NATD) from - * <b>options</b>. On success, set *<b>n_ports_out</b> to the number - * of ports that are listed, update the *Port_set values in +/** Parse all ports from <b>options</b>. On success, set *<b>n_ports_out</b> + * to the number of ports that are listed, update the *Port_set values in * <b>options</b>, and return 0. On failure, set *<b>msg</b> to a * description of the problem and return -1. * @@ -6884,21 +7134,22 @@ parse_ports(or_options_t *options, int validate_only, /* Update the *Port_set options. The !! here is to force a boolean out of an integer. */ options->ORPort_set = - !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER, 0); options->SocksPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER, 1); options->TransPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER, 1); options->NATDPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_NATD_LISTENER, 1); + /* Use options->ControlSocket to test if a control socket is set */ options->ControlPort_set = - !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_CONTROL_LISTENER, 0); options->DirPort_set = - !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER, 0); options->DNSPort_set = - !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER, 1); options->ExtORPort_set = - !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER); + !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER, 0); if (world_writable_control_socket) { SMARTLIST_FOREACH(ports, port_cfg_t *, p, diff --git a/src/or/config.h b/src/or/config.h index 7db66a31b9..6645532514 100644 --- a/src/or/config.h +++ b/src/or/config.h @@ -76,6 +76,8 @@ MOCK_DECL(char *, #define get_datadir_fname_suffix(sub1, suffix) \ get_datadir_fname2_suffix((sub1), NULL, (suffix)) +int using_default_dir_authorities(const or_options_t *options); + int check_or_create_data_subdir(const char *subdir); int write_to_data_subdir(const char* subdir, const char* fname, const char* str, const char* descr); @@ -128,7 +130,11 @@ int addressmap_register_auto(const char *from, const char *to, time_t expires, addressmap_entry_source_t addrmap_source, const char **msg); -int config_parse_unix_port(const char *addrport, char **path_out); + +int port_cfg_line_extract_addrport(const char *line, + char **addrport_out, + int *is_unix_out, + const char **rest_out); /** Represents the information stored in a torrc Bridge line. */ typedef struct bridge_line_t { @@ -166,6 +172,8 @@ extern struct config_format_t options_format; 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_single_onion(or_options_t *options, + char **msg); STATIC int options_validate(or_options_t *old_options, or_options_t *options, or_options_t *default_options, diff --git a/src/or/confparse.c b/src/or/confparse.c index efcf4f981e..ca54284dba 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -1,3 +1,4 @@ + /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. @@ -9,6 +10,16 @@ * * \brief Back-end for parsing and generating key-value files, used to * implement the torrc file format and the state file. + * + * This module is used by config.c to parse and encode torrc + * configuration files, and by statefile.c to parse and encode the + * $DATADIR/state file. + * + * To use this module, its callers provide an instance of + * config_format_t to describe the mappings from a set of configuration + * options to a number of fields in a C structure. With this mapping, + * the functions here can convert back and forth between the C structure + * specified, and a linked list of key-value pairs. */ #include "or.h" diff --git a/src/or/connection.c b/src/or/connection.c index 73a8691810..2e3df34a5a 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -8,6 +8,50 @@ * \file connection.c * \brief General high-level functions to handle reading and writing * on connections. + * + * Each connection (ideally) represents a TLS connection, a TCP socket, a unix + * socket, or a UDP socket on which reads and writes can occur. (But see + * connection_edge.c for cases where connections can also represent streams + * that do not have a corresponding socket.) + * + * The module implements the abstract type, connection_t. The subtypes are: + * <ul> + * <li>listener_connection_t, implemented here in connection.c + * <li>dir_connection_t, implemented in directory.c + * <li>or_connection_t, implemented in connection_or.c + * <li>edge_connection_t, implemented in connection_edge.c, along with + * its subtype(s): + * <ul><li>entry_connection_t, also implemented in connection_edge.c + * </ul> + * <li>control_connection_t, implemented in control.c + * </ul> + * + * The base type implemented in this module is responsible for basic + * rate limiting, flow control, and marshalling bytes onto and off of the + * network (either directly or via TLS). + * + * Connections are registered with the main loop with connection_add(). As + * they become able to read or write register the fact with the event main + * loop by calling connection_watch_events(), connection_start_reading(), or + * connection_start_writing(). When they no longer want to read or write, + * they call connection_stop_reading() or connection_start_writing(). + * + * To queue data to be written on a connection, call + * connection_write_to_buf(). When data arrives, the + * connection_process_inbuf() callback is invoked, which dispatches to a + * type-specific function (such as connection_edge_process_inbuf() for + * example). Connection types that need notice of when data has been written + * receive notification via connection_flushed_some() and + * connection_finished_flushing(). These functions all delegate to + * type-specific implementations. + * + * Additionally, beyond the core of connection_t, this module also implements: + * <ul> + * <li>Listeners, which wait for incoming sockets and launch connections + * <li>Outgoing SOCKS proxy support + * <li>Outgoing HTTP proxy support + * <li>An out-of-sockets handler for dealing with socket exhaustion + * </ul> **/ #define CONNECTION_PRIVATE diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 2726349673..875c911f01 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1,4 +1,4 @@ - /* 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-2016, The Tor Project, Inc. */ @@ -7,6 +7,51 @@ /** * \file connection_edge.c * \brief Handle edge streams. + * + * An edge_connection_t is a subtype of a connection_t, and represents two + * critical concepts in Tor: a stream, and an edge connection. From the Tor + * protocol's point of view, a stream is a bi-directional channel that is + * multiplexed on a single circuit. Each stream on a circuit is identified + * with a separate 16-bit stream ID, local to the (circuit,exit) pair. + * Streams are created in response to client requests. + * + * An edge connection is one thing that can implement a stream: it is either a + * TCP application socket that has arrived via (e.g.) a SOCKS request, or an + * exit connection. + * + * Not every instance of edge_connection_t truly represents an edge connction, + * however. (Sorry!) We also create edge_connection_t objects for streams that + * we will not be handling with TCP. The types of these streams are: + * <ul> + * <li>DNS lookup streams, created on the client side in response to + * a UDP DNS request received on a DNSPort, or a RESOLVE command + * on a controller. + * <li>DNS lookup streams, created on the exit side in response to + * a RELAY_RESOLVE cell from a client. + * <li>Tunneled directory streams, created on the directory cache side + * in response to a RELAY_BEGINDIR cell. These streams attach directly + * to a dir_connection_t object without ever using TCP. + * </ul> + * + * This module handles general-purpose functionality having to do with + * edge_connection_t. On the client side, it accepts various types of + * application requests on SocksPorts, TransPorts, and NATDPorts, and + * creates streams appropriately. + * + * This module is also responsible for implementing stream isolation: + * ensuring that streams that should not be linkable to one another are + * kept to different circuits. + * + * On the exit side, this module handles the various stream-creating + * type of RELAY cells by launching appropriate outgoing connections, + * DNS requests, or directory connection objects. + * + * And for all edge connections, this module is responsible for handling + * incoming and outdoing data as it arrives or leaves in the relay.c + * module. (Outgoing data will be packaged in + * connection_edge_process_inbuf() as it calls + * connection_edge_package_raw_inbuf(); incoming data from RELAY_DATA + * cells is applied in connection_edge_process_relay_cell().) **/ #define CONNECTION_EDGE_PRIVATE @@ -27,6 +72,7 @@ #include "control.h" #include "dns.h" #include "dnsserv.h" +#include "directory.h" #include "dirserv.h" #include "hibernate.h" #include "hs_common.h" @@ -785,7 +831,8 @@ connection_ap_rescan_and_attach_pending(void) #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. + * again. If there is an available circuit for a stream, attach it. Otherwise, + * launch a new circuit. * * 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. @@ -800,8 +847,9 @@ connection_ap_attach_pending(int retry) if (untried_pending_connections == 0 && !retry) return; - /* Don't allow modifications to pending_entry_connections while we are - * iterating over it. */ + /* Don't allow any modifications to list while we are iterating over + * it. We'll put streams back on this list if we can't attach them + * immediately. */ smartlist_t *pending = pending_entry_connections; pending_entry_connections = smartlist_new(); @@ -828,6 +876,7 @@ connection_ap_attach_pending(int retry) continue; } + /* Okay, we're through the sanity checks. Try to handle this stream. */ if (connection_ap_handshake_attach_circuit(entry_conn) < 0) { if (!conn->marked_for_close) connection_mark_unattached_ap(entry_conn, @@ -837,12 +886,17 @@ connection_ap_attach_pending(int retry) if (! conn->marked_for_close && conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_CIRCUIT_WAIT) { + /* Is it still waiting for a circuit? If so, we didn't attach it, + * so it's still pending. Put it back on the list. + */ if (!smartlist_contains(pending_entry_connections, entry_conn)) { smartlist_add(pending_entry_connections, entry_conn); continue; } } + /* If we got here, then we either closed the connection, or + * we attached it. */ UNMARK(); } SMARTLIST_FOREACH_END(entry_conn); @@ -892,6 +946,15 @@ connection_ap_mark_as_pending_circuit_(entry_connection_t *entry_conn, untried_pending_connections = 1; smartlist_add(pending_entry_connections, entry_conn); + + /* Work-around for bug 19969: we handle pending_entry_connections at + * the end of run_main_loop_once(), but in many cases that function will + * take a very long time, if ever, to finish its call to event_base_loop(). + * + * So the fix is to tell it right now that it ought to finish its loop at + * its next available opportunity. + */ + tell_event_loop_to_finish(); } /** Mark <b>entry_conn</b> as no longer waiting for a circuit. */ @@ -1141,6 +1204,8 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, /* Remember the original address so we can tell the user about what * they actually said, not just what it turned into. */ + /* XXX yes, this is the same as out->orig_address above. One is + * in the output, and one is in the connection. */ if (! conn->original_dest_address) { /* Is the 'if' necessary here? XXXX */ conn->original_dest_address = tor_strdup(conn->socks_request->address); @@ -1148,7 +1213,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, /* First, apply MapAddress and MAPADDRESS mappings. We need to do * these only for non-reverse lookups, since they don't exist for those. - * We need to do this before we consider automapping, since we might + * We also need to do this before we consider automapping, since we might * e.g. resolve irc.oftc.net into irconionaddress.onion, at which point * we'd need to automap it. */ if (socks->command != SOCKS_COMMAND_RESOLVE_PTR) { @@ -1160,9 +1225,12 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, } } - /* Now, handle automapping. Automapping happens when we're asked to - * resolve a hostname, and AutomapHostsOnResolve is set, and - * the hostname has a suffix listed in AutomapHostsSuffixes. + /* Now see if we need to create or return an existing Hostname->IP + * automapping. Automapping happens when we're asked to resolve a + * hostname, and AutomapHostsOnResolve is set, and the hostname has a + * suffix listed in AutomapHostsSuffixes. It's a handy feature + * that lets you have Tor assign e.g. IPv6 addresses for .onion + * names, and return them safely from DNSPort. */ if (socks->command == SOCKS_COMMAND_RESOLVE && tor_addr_parse(&addr_tmp, socks->address)<0 && @@ -1202,7 +1270,8 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, } /* Now handle reverse lookups, if they're in the cache. This doesn't - * happen too often, since client-side DNS caching is off by default. */ + * happen too often, since client-side DNS caching is off by default, + * and very deprecated. */ if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) { unsigned rewrite_flags = 0; if (conn->entry_cfg.use_cached_ipv4_answers) @@ -1228,7 +1297,7 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, } /* Hang on, did we find an answer saying that this is a reverse lookup for - * an internal address? If so, we should reject it if we're condigured to + * an internal address? If so, we should reject it if we're configured to * do so. */ if (options->ClientDNSRejectInternalAddresses) { /* Don't let people try to do a reverse lookup on 10.0.0.1. */ @@ -1247,11 +1316,12 @@ connection_ap_handshake_rewrite(entry_connection_t *conn, } } - /* If we didn't automap it before, then this is still the address - * that came straight from the user, mapped according to any - * MapAddress/MAPADDRESS commands. Now other mappings, including - * previously registered Automap entries, TrackHostExits entries, - * and client-side DNS cache entries (not recommended). + /* If we didn't automap it before, then this is still the address that + * came straight from the user, mapped according to any + * MapAddress/MAPADDRESS commands. Now apply other mappings, + * including previously registered Automap entries (IP back to + * hostname), TrackHostExits entries, and client-side DNS cache + * entries (if they're turned on). */ if (socks->command != SOCKS_COMMAND_RESOLVE_PTR && !out->automap) { @@ -1316,11 +1386,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, time_t now = time(NULL); rewrite_result_t rr; + /* First we'll do the rewrite part. Let's see if we get a reasonable + * answer. + */ memset(&rr, 0, sizeof(rr)); connection_ap_handshake_rewrite(conn,&rr); if (rr.should_close) { - /* connection_ap_handshake_rewrite told us to close the connection, + /* connection_ap_handshake_rewrite told us to close the connection: * either because it sent back an answer, or because it sent back an * error */ connection_mark_unattached_ap(conn, rr.end_reason); @@ -1334,8 +1407,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, const int automap = rr.automap; const addressmap_entry_source_t exit_source = rr.exit_source; - /* Parse the address provided by SOCKS. Modify it in-place if it - * specifies a hidden-service (.onion) or particular exit node (.exit). + /* Now, we parse the address to see if it's an .onion or .exit or + * other special address. */ const hostname_type_t addresstype = parse_extended_hostname(socks->address); @@ -1349,8 +1422,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } /* If this is a .exit hostname, strip off the .name.exit part, and - * see whether we're going to connect there, and otherwise handle it. - * (The ".exit" part got stripped off by "parse_extended_hostname"). + * see whether we're willing to connect there, and and otherwise handle the + * .exit address. * * We'll set chosen_exit_name and/or close the connection as appropriate. */ @@ -1362,7 +1435,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, const node_t *node = NULL; /* If this .exit was added by an AUTOMAP, then it came straight from - * a user. Make sure that options->AllowDotExit permits that. */ + * a user. Make sure that options->AllowDotExit permits that! */ if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) { /* Whoops; this one is stale. It must have gotten added earlier, * when AllowDotExit was on. */ @@ -1391,7 +1464,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } tor_assert(!automap); - /* Now, find the character before the .(name) part. */ + + /* Now, find the character before the .(name) part. + * (The ".exit" part got stripped off by "parse_extended_hostname"). + * + * We're going to put the exit name into conn->chosen_exit_name, and + * look up a node correspondingly. */ char *s = strrchr(socks->address,'.'); if (s) { /* The address was of the form "(stuff).(name).exit */ @@ -1447,10 +1525,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, implies no. */ } - /* Now, handle everything that isn't a .onion address. */ + /* Now, we handle everything that isn't a .onion address. */ if (addresstype != ONION_HOSTNAME) { /* Not a hidden-service request. It's either a hostname or an IP, - * possibly with a .exit that we stripped off. */ + * possibly with a .exit that we stripped off. We're going to check + * if we're allowed to connect/resolve there, and then launch the + * appropriate request. */ /* Check for funny characters in the address. */ if (address_is_invalid_destination(socks->address, 1)) { @@ -1467,14 +1547,68 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, /* If we're running in Tor2webMode, we don't allow anything BUT .onion * addresses. */ if (options->Tor2webMode) { - log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s " - "because tor2web mode is enabled.", + log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname " + "or IP address %s because tor2web mode is enabled.", safe_str_client(socks->address)); connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); return -1; } #endif + /* socks->address is a non-onion hostname or IP address. + * If we can't do any non-onion requests, refuse the connection. + * If we have a hostname but can't do DNS, refuse the connection. + * If we have an IP address, but we can't use that address family, + * refuse the connection. + * + * If we can do DNS requests, and we can use at least one address family, + * then we have to resolve the address first. Then we'll know if it + * resolves to a usable address family. */ + + /* First, check if all non-onion traffic is disabled */ + if (!conn->entry_cfg.dns_request && !conn->entry_cfg.ipv4_traffic + && !conn->entry_cfg.ipv6_traffic) { + log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname " + "or IP address %s because Port has OnionTrafficOnly set (or " + "NoDNSRequest, NoIPv4Traffic, and NoIPv6Traffic).", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + + /* Then check if we have a hostname or IP address, and whether DNS or + * the IP address family are permitted. Reject if not. */ + tor_addr_t dummy_addr; + int socks_family = tor_addr_parse(&dummy_addr, socks->address); + /* family will be -1 for a non-onion hostname that's not an IP */ + if (socks_family == -1) { + if (!conn->entry_cfg.dns_request) { + log_warn(LD_APP, "Refusing to connect to hostname %s " + "because Port has NoDNSRequest set.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + } else if (socks_family == AF_INET) { + if (!conn->entry_cfg.ipv4_traffic) { + log_warn(LD_APP, "Refusing to connect to IPv4 address %s because " + "Port has NoIPv4Traffic set.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + } else if (socks_family == AF_INET6) { + if (!conn->entry_cfg.ipv6_traffic) { + log_warn(LD_APP, "Refusing to connect to IPv6 address %s because " + "Port has NoIPv6Traffic set.", + safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + } else { + tor_assert_nonfatal_unreached_once(); + } + /* See if this is a hostname lookup that we can answer immediately. * (For example, an attempt to look up the IP address for an IP address.) */ @@ -1493,7 +1627,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } tor_assert(!automap); rep_hist_note_used_resolve(now); /* help predict this next time */ - } else if (socks->command == SOCKS_COMMAND_CONNECT) { + } + + /* Now see if this is a connect request that we can reject immediately */ + if (socks->command == SOCKS_COMMAND_CONNECT) { /* Special handling for attempts to connect */ tor_assert(!automap); /* Don't allow connections to port 0. */ @@ -1547,7 +1684,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } /* end "if we should check for internal addresses" */ /* Okay. We're still doing a CONNECT, and it wasn't a private - * address. Do special handling for literal IP addresses */ + * address. Here we do special handling for literal IP addresses, + * to see if we should reject this preemptively, and to set up + * fields in conn->entry_cfg to tell the exit what AF we want. */ { tor_addr_t addr; /* XXX Duplicate call to tor_addr_parse. */ @@ -1590,11 +1729,15 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } } + /* we never allow IPv6 answers on socks4. (TODO: Is this smart?) */ if (socks->socks_version == 4) conn->entry_cfg.ipv6_traffic = 0; /* Still handling CONNECT. Now, check for exit enclaves. (Which we - * don't do on BEGINDIR, or there is a chosen exit.) + * don't do on BEGINDIR, or when there is a chosen exit.) + * + * TODO: Should we remove this? Exit enclaves are nutty and don't + * work very well */ if (!conn->use_begindir && !conn->chosen_exit_name && !circ) { /* see if we can find a suitable enclave exit */ @@ -1618,7 +1761,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, if (consider_plaintext_ports(conn, socks->port) < 0) return -1; - /* Remember the port so that we do predicted requests there. */ + /* Remember the port so that we will predict that more requests + there will happen in the future. */ if (!conn->use_begindir) { /* help predict this next time */ rep_hist_note_used_port(now, socks->port); @@ -1643,6 +1787,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, if (circ) { rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath); } else { + /* We'll try to attach it at the next event loop, or whenever + * we call connection_ap_attach_pending() */ connection_ap_mark_as_pending_circuit(conn); rv = 0; } @@ -1662,6 +1808,14 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, /* If we get here, it's a request for a .onion address! */ tor_assert(!automap); + /* If .onion address requests are disabled, refuse the request */ + if (!conn->entry_cfg.onion_traffic) { + log_warn(LD_APP, "Onion address %s requested from a port with .onion " + "disabled", safe_str_client(socks->address)); + connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY); + return -1; + } + /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those * for hidden service addresses. */ if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) { @@ -1712,8 +1866,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, log_info(LD_REND,"Got a hidden service request for ID '%s'", safe_str_client(onion_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. */ + /* Lookup the given onion address. If invalid, stop right now. + * Otherwise, we might have it in the cache or not. */ unsigned int refetch_desc = 0; rend_cache_entry_t *entry = NULL; const int rend_cache_lookup_result = @@ -1727,6 +1881,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); return -1; case ENOENT: + /* We didn't have this; we should look it up. */ refetch_desc = 1; break; default: @@ -1736,8 +1891,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, } } - /* Help predict this next time. We're not sure if it will need - * a stable circuit yet, but we know we'll need *something*. */ + /* Help predict that we'll want to do hidden service circuits in the + * future. 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); /* Now we have a descriptor but is it usable or not? If not, refetch. @@ -1752,9 +1908,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, return 0; } - /* We have the descriptor so launch a connection to the HS. */ + /* 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."); + + /* We'll try to attach it at the next event loop, or whenever + * we call connection_ap_attach_pending() */ connection_ap_mark_as_pending_circuit(conn); return 0; } @@ -2273,6 +2432,7 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) char payload[CELL_PAYLOAD_SIZE]; int payload_len; int begin_type; + const or_options_t *options = get_options(); origin_circuit_t *circ; edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(ap_conn); connection_t *base_conn = TO_CONN(edge_conn); @@ -2316,10 +2476,33 @@ connection_ap_handshake_send_begin(entry_connection_t *ap_conn) begin_type = ap_conn->use_begindir ? RELAY_COMMAND_BEGIN_DIR : RELAY_COMMAND_BEGIN; + + /* Check that circuits are anonymised, based on their type. */ if (begin_type == RELAY_COMMAND_BEGIN) { -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(circ->build_state->onehop_tunnel == 0); -#endif + /* This connection is a standard OR connection. + * Make sure its path length is anonymous, or that we're in a + * non-anonymous mode. */ + assert_circ_anonymity_ok(circ, options); + } else if (begin_type == RELAY_COMMAND_BEGIN_DIR) { + /* This connection is a begindir directory connection. + * Look at the linked directory connection to access the directory purpose. + * (This must be non-NULL, because we're doing begindir.) */ + tor_assert(base_conn->linked); + connection_t *linked_dir_conn_base = base_conn->linked_conn; + tor_assert(linked_dir_conn_base); + /* Sensitive directory connections must have an anonymous path length. + * Otherwise, directory connections are typically one-hop. + * This matches the earlier check for directory connection path anonymity + * in directory_initiate_command_rend(). */ + if (purpose_needs_anonymity(linked_dir_conn_base->purpose, + TO_DIR_CONN(linked_dir_conn_base)->router_purpose, + TO_DIR_CONN(linked_dir_conn_base)->requested_resource)) { + assert_circ_anonymity_ok(circ, options); + } + } else { + /* This code was written for the two connection types BEGIN and BEGIN_DIR + */ + tor_assert_unreached(); } if (connection_edge_send_command(edge_conn, begin_type, @@ -3142,6 +3325,24 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) return 0; } +/** Helper: Return true and set *<b>why_rejected</b> to an optional clarifying + * message message iff we do not allow connections to <b>addr</b>:<b>port</b>. + */ +static int +my_exit_policy_rejects(const tor_addr_t *addr, + uint16_t port, + const char **why_rejected) +{ + if (router_compare_to_my_exit_policy(addr, port)) { + *why_rejected = ""; + return 1; + } else if (tor_addr_family(addr) == AF_INET6 && !get_options()->IPv6Exit) { + *why_rejected = " (IPv6 address without IPv6Exit configured)"; + return 1; + } + return 0; +} + /** Connect to conn's specified addr and port. If it worked, conn * has now been added to the connection_array. * @@ -3156,14 +3357,18 @@ connection_exit_connect(edge_connection_t *edge_conn) uint16_t port; connection_t *conn = TO_CONN(edge_conn); int socket_error = 0, result; - - if ( (!connection_edge_is_rendezvous_stream(edge_conn) && - router_compare_to_my_exit_policy(&edge_conn->base_.addr, - edge_conn->base_.port)) || - (tor_addr_family(&conn->addr) == AF_INET6 && - ! get_options()->IPv6Exit)) { - log_info(LD_EXIT,"%s:%d failed exit policy. Closing.", - escaped_safe_str_client(conn->address), conn->port); + const char *why_failed_exit_policy = NULL; + + /* Apply exit policy to non-rendezvous connections. */ + if (! connection_edge_is_rendezvous_stream(edge_conn) && + my_exit_policy_rejects(&edge_conn->base_.addr, + edge_conn->base_.port, + &why_failed_exit_policy)) { + if (BUG(!why_failed_exit_policy)) + why_failed_exit_policy = ""; + log_info(LD_EXIT,"%s:%d failed exit policy%s. Closing.", + escaped_safe_str_client(conn->address), conn->port, + why_failed_exit_policy); connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); connection_free(conn); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 72d8e13e90..eb67f0653f 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -8,6 +8,17 @@ * \file connection_or.c * \brief Functions to handle OR connections, TLS handshaking, and * cells on the network. + * + * An or_connection_t is a subtype of connection_t (as implemented in + * connection.c) that uses a TLS connection to send and receive cells on the + * Tor network. (By sending and receiving cells connection_or.c, it cooperates + * with channeltls.c to implement a the channel interface of channel.c.) + * + * Every OR connection has an underlying tortls_t object (as implemented in + * tortls.c) which it uses as its TLS stream. It is responsible for + * sending and receiving cells over that TLS. + * + * This module also implements the client side of the v3 Tor link handshake, **/ #include "or.h" #include "buffers.h" @@ -38,9 +49,11 @@ #include "relay.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "ext_orport.h" #include "scheduler.h" +#include "torcert.h" static int connection_tls_finish_handshake(or_connection_t *conn); static int connection_or_launch_v3_or_handshake(or_connection_t *conn); @@ -132,15 +145,18 @@ connection_or_clear_identity_map(void) /** Change conn->identity_digest to digest, and add conn into * orconn_digest_map. */ static void -connection_or_set_identity_digest(or_connection_t *conn, const char *digest) +connection_or_set_identity_digest(or_connection_t *conn, + const char *rsa_digest, + const ed25519_public_key_t *ed_id) { + (void) ed_id; // DOCDOC // XXXX not implemented yet. or_connection_t *tmp; tor_assert(conn); - tor_assert(digest); + tor_assert(rsa_digest); if (!orconn_identity_map) orconn_identity_map = digestmap_new(); - if (tor_memeq(conn->identity_digest, digest, DIGEST_LEN)) + if (tor_memeq(conn->identity_digest, rsa_digest, DIGEST_LEN)) return; /* If the identity was set previously, remove the old mapping. */ @@ -150,23 +166,23 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest) channel_clear_identity_digest(TLS_CHAN_TO_BASE(conn->chan)); } - memcpy(conn->identity_digest, digest, DIGEST_LEN); + memcpy(conn->identity_digest, rsa_digest, DIGEST_LEN); /* If we're setting the ID to zero, don't add a mapping. */ - if (tor_digest_is_zero(digest)) + if (tor_digest_is_zero(rsa_digest)) return; - tmp = digestmap_set(orconn_identity_map, digest, conn); + tmp = digestmap_set(orconn_identity_map, rsa_digest, conn); conn->next_with_same_id = tmp; /* Deal with channels */ if (conn->chan) - channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), digest); + channel_set_identity_digest(TLS_CHAN_TO_BASE(conn->chan), rsa_digest); #if 1 /* Testing code to check for bugs in representation. */ for (; tmp; tmp = tmp->next_with_same_id) { - tor_assert(tor_memeq(tmp->identity_digest, digest, DIGEST_LEN)); + tor_assert(tor_memeq(tmp->identity_digest, rsa_digest, DIGEST_LEN)); tor_assert(tmp != conn); } #endif @@ -864,10 +880,12 @@ void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, const char *id_digest, + const ed25519_public_key_t *ed_id, int started_here) { + (void) ed_id; // not fully used yet. const node_t *r = node_get_by_id(id_digest); - connection_or_set_identity_digest(conn, id_digest); + connection_or_set_identity_digest(conn, id_digest, ed_id); connection_or_update_token_buckets_helper(conn, 1, get_options()); conn->base_.port = port; @@ -1160,8 +1178,11 @@ connection_or_notify_error(or_connection_t *conn, MOCK_IMPL(or_connection_t *, connection_or_connect, (const tor_addr_t *_addr, uint16_t port, - const char *id_digest, channel_tls_t *chan)) + const char *id_digest, + const ed25519_public_key_t *ed_id, + channel_tls_t *chan)) { + (void) ed_id; // XXXX not fully used yet. or_connection_t *conn; const or_options_t *options = get_options(); int socket_error = 0; @@ -1192,7 +1213,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, */ conn->chan = chan; chan->conn = conn; - connection_or_init_conn_from_address(conn, &addr, port, id_digest, 1); + connection_or_init_conn_from_address(conn, &addr, port, id_digest, ed_id, 1); connection_or_change_state(conn, OR_CONN_STATE_CONNECTING); control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0); @@ -1551,7 +1572,9 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, if (started_here) return connection_or_client_learned_peer_id(conn, - (const uint8_t*)digest_rcvd_out); + (const uint8_t*)digest_rcvd_out, + NULL // Ed25519 ID + ); return 0; } @@ -1581,12 +1604,16 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, */ int connection_or_client_learned_peer_id(or_connection_t *conn, - const uint8_t *peer_id) + const uint8_t *rsa_peer_id, + const ed25519_public_key_t *ed_peer_id) { + (void) ed_peer_id; // not used yet. + const or_options_t *options = get_options(); if (tor_digest_is_zero(conn->identity_digest)) { - connection_or_set_identity_digest(conn, (const char*)peer_id); + connection_or_set_identity_digest(conn, + (const char*)rsa_peer_id, ed_peer_id); tor_free(conn->nickname); conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); conn->nickname[0] = '$'; @@ -1598,14 +1625,14 @@ connection_or_client_learned_peer_id(or_connection_t *conn, /* if it's a bridge and we didn't know its identity fingerprint, now * we do -- remember it for future attempts. */ learned_router_identity(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); + (const char*)rsa_peer_id /*, ed_peer_id XXXX */); } - if (tor_memneq(peer_id, conn->identity_digest, DIGEST_LEN)) { + if (tor_memneq(rsa_peer_id, conn->identity_digest, DIGEST_LEN)) { /* I was aiming for a particular digest. I didn't get it! */ char seen[HEX_DIGEST_LEN+1]; char expected[HEX_DIGEST_LEN+1]; - base16_encode(seen, sizeof(seen), (const char*)peer_id, DIGEST_LEN); + base16_encode(seen, sizeof(seen), (const char*)rsa_peer_id, DIGEST_LEN); base16_encode(expected, sizeof(expected), conn->identity_digest, DIGEST_LEN); const int using_hardcoded_fingerprints = @@ -1658,7 +1685,7 @@ connection_or_client_learned_peer_id(or_connection_t *conn, } if (authdir_mode_tests_reachability(options)) { dirserv_orconn_tls_done(&conn->base_.addr, conn->base_.port, - (const char*)peer_id); + (const char*)rsa_peer_id /*, ed_id XXXX */); } return 0; @@ -1714,7 +1741,8 @@ connection_tls_finish_handshake(or_connection_t *conn) if (tor_tls_used_v1_handshake(conn->tls)) { conn->link_proto = 1; connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); + conn->base_.port, digest_rcvd, + NULL, 0); tor_tls_block_renegotiation(conn->tls); rep_hist_note_negotiated_link_proto(1, started_here); return connection_or_set_state_open(conn); @@ -1723,7 +1751,8 @@ connection_tls_finish_handshake(or_connection_t *conn) if (connection_init_or_handshake_state(conn, started_here) < 0) return -1; connection_or_init_conn_from_address(conn, &conn->base_.addr, - conn->base_.port, digest_rcvd, 0); + conn->base_.port, digest_rcvd, + NULL, 0); return connection_or_send_versions(conn, 0); } } @@ -1762,6 +1791,8 @@ connection_init_or_handshake_state(or_connection_t *conn, int started_here) s->started_here = started_here ? 1 : 0; s->digest_sent_data = 1; s->digest_received_data = 1; + s->certs = or_handshake_certs_new(); + s->certs->started_here = s->started_here; return 0; } @@ -1773,8 +1804,7 @@ or_handshake_state_free(or_handshake_state_t *state) return; crypto_digest_free(state->digest_sent); crypto_digest_free(state->digest_received); - tor_x509_cert_free(state->auth_cert); - tor_x509_cert_free(state->id_cert); + or_handshake_certs_free(state->certs); memwipe(state, 0xBE, sizeof(or_handshake_state_t)); tor_free(state); } @@ -2121,57 +2151,171 @@ connection_or_send_netinfo,(or_connection_t *conn)) return 0; } +/** Helper used to add an encoded certs to a cert cell */ +static void +add_certs_cell_cert_helper(certs_cell_t *certs_cell, + uint8_t cert_type, + const uint8_t *cert_encoded, + size_t cert_len) +{ + tor_assert(cert_len <= UINT16_MAX); + certs_cell_cert_t *ccc = certs_cell_cert_new(); + ccc->cert_type = cert_type; + ccc->cert_len = cert_len; + certs_cell_cert_setlen_body(ccc, cert_len); + memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len); + + certs_cell_add_certs(certs_cell, ccc); +} + +/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at + * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are + * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>. */ +static void +add_x509_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_x509_cert_t *cert) +{ + if (NULL == cert) + return; + + const uint8_t *cert_encoded = NULL; + size_t cert_len; + tor_x509_cert_get_der(cert, &cert_encoded, &cert_len); + + add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len); +} + +/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object + * that we are building in <b>certs_cell</b>. Set its type field to + * <b>cert_type</b>. */ +static void +add_ed25519_cert(certs_cell_t *certs_cell, + uint8_t cert_type, + const tor_cert_t *cert) +{ + if (NULL == cert) + return; + + add_certs_cell_cert_helper(certs_cell, cert_type, + cert->encoded, cert->encoded_len); +} + /** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1 * on failure. */ int connection_or_send_certs_cell(or_connection_t *conn) { 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; - size_t cell_len; - ssize_t pos; + + certs_cell_t *certs_cell = NULL; tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3); if (! conn->handshake_state) return -1; + const int conn_in_server_mode = ! conn->handshake_state->started_here; + + /* Get the encoded values of the X509 certificates */ if (tor_tls_get_my_certs(conn_in_server_mode, &link_cert, &id_cert) < 0) return -1; - 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 */ + - link_len + id_len; - cell = var_cell_new(cell_len); - cell->command = CELL_CERTS; - cell->payload[0] = 2; - pos = 1; + tor_assert(link_cert); + tor_assert(id_cert); - if (conn_in_server_mode) - cell->payload[pos] = OR_CERT_TYPE_TLS_LINK; /* Link cert */ - else - cell->payload[pos] = OR_CERT_TYPE_AUTH_1024; /* client authentication */ - set_uint16(&cell->payload[pos+1], htons(link_len)); - memcpy(&cell->payload[pos+3], link_encoded, link_len); - pos += 3 + link_len; + certs_cell = certs_cell_new(); + + /* Start adding certs. First the link cert or auth1024 cert. */ + if (conn_in_server_mode) { + add_x509_cert(certs_cell, + OR_CERT_TYPE_TLS_LINK, link_cert); + } else { + add_x509_cert(certs_cell, + OR_CERT_TYPE_AUTH_1024, link_cert); + } - cell->payload[pos] = OR_CERT_TYPE_ID_1024; /* ID cert */ - set_uint16(&cell->payload[pos+1], htons(id_len)); - memcpy(&cell->payload[pos+3], id_encoded, id_len); - pos += 3 + id_len; + /* Next the RSA->RSA ID cert */ + add_x509_cert(certs_cell, + OR_CERT_TYPE_ID_1024, id_cert); + + /* Next the Ed25519 certs */ + add_ed25519_cert(certs_cell, + CERTTYPE_ED_ID_SIGN, + get_master_signing_key_cert()); + if (conn_in_server_mode) { + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_LINK, + get_current_link_cert_cert()); + } else { + add_ed25519_cert(certs_cell, + CERTTYPE_ED_SIGN_AUTH, + get_current_auth_key_cert()); + } + + /* And finally the crosscert. */ + { + const uint8_t *crosscert=NULL; + size_t crosscert_len; + get_master_rsa_crosscert(&crosscert, &crosscert_len); + if (crosscert) { + add_certs_cell_cert_helper(certs_cell, + CERTTYPE_RSA1024_ID_EDID, + crosscert, crosscert_len); + } + } + + /* We've added all the certs; make the cell. */ + certs_cell->n_certs = certs_cell_getlen_certs(certs_cell); - tor_assert(pos == (int)cell_len); /* Otherwise we just smashed the heap */ + ssize_t alloc_len = certs_cell_encoded_len(certs_cell); + tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX); + cell = var_cell_new(alloc_len); + cell->command = CELL_CERTS; + ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell); + tor_assert(enc_len > 0 && enc_len <= alloc_len); + cell->payload_len = enc_len; connection_or_write_var_cell_to_buf(cell, conn); var_cell_free(cell); + certs_cell_free(certs_cell); return 0; } +/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that + * we can send and receive. */ +int +authchallenge_type_is_supported(uint16_t challenge_type) +{ + switch (challenge_type) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + case AUTHTYPE_ED25519_SHA256_RFC5705: + return 1; + case AUTHTYPE_RSA_SHA256_RFC5705: + default: + return 0; + } +} + +/** Return true iff <b>challenge_type_a</b> is one that we would rather + * use than <b>challenge_type_b</b>. */ +int +authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b) +{ + /* Any supported type is better than an unsupported one; + * all unsupported types are equally bad. */ + if (!authchallenge_type_is_supported(challenge_type_a)) + return 0; + if (!authchallenge_type_is_supported(challenge_type_b)) + return 1; + /* It happens that types are superior in numerically ascending order. + * If that ever changes, this must change too. */ + return (challenge_type_a > challenge_type_b); +} + /** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0 * on success, -1 on failure. */ int @@ -2186,17 +2330,26 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) auth_challenge_cell_t *ac = auth_challenge_cell_new(); + tor_assert(sizeof(ac->challenge) == 32); crypto_rand((char*)ac->challenge, sizeof(ac->challenge)); auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET); + /* Disabled, because everything that supports this method also supports + * the much-superior ED25519_SHA256_RFC5705 */ + /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */ + auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705); 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) + if (len != cell->payload_len) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Encoded auth challenge cell length not as expected"); goto done; + /* LCOV_EXCL_STOP */ + } cell->command = CELL_AUTH_CHALLENGE; connection_or_write_var_cell_to_buf(cell, conn); @@ -2210,8 +2363,8 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) } /** Compute the main body of an AUTHENTICATE cell that a client can use - * to authenticate itself on a v3 handshake for <b>conn</b>. Write it to the - * <b>outlen</b>-byte buffer at <b>out</b>. + * to authenticate itself on a v3 handshake for <b>conn</b>. Return it + * in a var_cell_t. * * If <b>server</b> is true, only calculate the first * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's @@ -2227,24 +2380,44 @@ connection_or_send_auth_challenge_cell(or_connection_t *conn) * * Return the length of the cell body on success, and -1 on failure. */ -int +var_cell_t * connection_or_compute_authenticate_cell_body(or_connection_t *conn, - uint8_t *out, size_t outlen, + const int authtype, crypto_pk_t *signing_key, - int server) + const ed25519_keypair_t *ed_signing_key, + int server) { auth1_t *auth = NULL; auth_ctx_t *ctx = auth_ctx_new(); - int result; + var_cell_t *result = NULL; + int old_tlssecrets_algorithm = 0; + const char *authtype_str = NULL; - /* assert state is reasonable XXXX */ + int is_ed = 0; - ctx->is_ed = 0; + /* assert state is reasonable XXXX */ + switch (authtype) { + case AUTHTYPE_RSA_SHA256_TLSSECRET: + authtype_str = "AUTH0001"; + old_tlssecrets_algorithm = 1; + break; + case AUTHTYPE_RSA_SHA256_RFC5705: + authtype_str = "AUTH0002"; + break; + case AUTHTYPE_ED25519_SHA256_RFC5705: + authtype_str = "AUTH0003"; + is_ed = 1; + break; + default: + tor_assert(0); + break; + } auth = auth1_new(); + ctx->is_ed = is_ed; /* Type: 8 bytes. */ - memcpy(auth1_getarray_type(auth), "AUTH0001", 8); + memcpy(auth1_getarray_type(auth), authtype_str, 8); { const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL; @@ -2254,7 +2427,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, 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_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert); tor_assert(my_digests); tor_assert(their_digests); my_id = (uint8_t*)my_digests->d[DIGEST_SHA256]; @@ -2270,6 +2443,22 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, memcpy(auth->sid, server_id, 32); } + if (is_ed) { + const ed25519_public_key_t *my_ed_id, *their_ed_id; + if (!conn->handshake_state->certs->ed_id_sign) { + log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer."); + goto err; + } + my_ed_id = get_master_identity_key(); + their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key; + + const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey; + const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey; + + memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN); + memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN); + } + { crypto_digest_t *server_d, *client_d; if (server) { @@ -2298,7 +2487,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, cert = freecert; } if (!cert) { - log_warn(LD_OR, "Unable to find cert when making AUTH1 data."); + log_warn(LD_OR, "Unable to find cert when making %s data.", + authtype_str); goto err; } @@ -2310,36 +2500,79 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } /* HMAC of clientrandom and serverrandom using master key : 32 octets */ - tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); + if (old_tlssecrets_algorithm) { + tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets); + } else { + char label[128]; + tor_snprintf(label, sizeof(label), + "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str); + tor_tls_export_key_material(conn->tls, auth->tlssecrets, + auth->cid, sizeof(auth->cid), + label); + } /* 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*)auth->rand, 24); + ssize_t maxlen = auth1_encoded_len(auth, ctx); + if (ed_signing_key && is_ed) { + maxlen += ED25519_SIG_LEN; + } else if (signing_key && !is_ed) { + maxlen += crypto_pk_keysize(signing_key); + } + + const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */ + result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen); + uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN; + const size_t outlen = maxlen; ssize_t len; + + result->command = CELL_AUTHENTICATE; + set_uint16(result->payload, htons(authtype)); + if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) { - log_warn(LD_OR, "Unable to encode signed part of AUTH1 data."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data."); goto err; + /* LCOV_EXCL_STOP */ } 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."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that " + "we just encoded"); goto err; + /* LCOV_EXCL_STOP */ } - result = (int) (tmp->end_of_fixed_part - out); + result->payload_len = (tmp->end_of_signed - result->payload); + auth1_free(tmp); if (len2 != len) { - log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data."); + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data."); goto err; + /* LCOV_EXCL_STOP */ } goto done; } - if (signing_key) { + if (ed_signing_key && is_ed) { + ed25519_signature_t sig; + if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to sign ed25519 authentication data"); + goto err; + /* LCOV_EXCL_STOP */ + } + auth1_setlen_sig(auth, ED25519_SIG_LEN); + memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN); + + } else if (signing_key && !is_ed) { auth1_setlen_sig(auth, crypto_pk_keysize(signing_key)); char d[32]; @@ -2354,18 +2587,24 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } auth1_setlen_sig(auth, siglen); + } - len = auth1_encode(out, outlen, auth, ctx); - if (len < 0) { - log_warn(LD_OR, "Unable to encode signed AUTH1 data."); - goto err; - } + len = auth1_encode(out, outlen, auth, ctx); + if (len < 0) { + /* LCOV_EXCL_START */ + log_warn(LD_BUG, "Unable to encode signed AUTH1 data."); + goto err; + /* LCOV_EXCL_STOP */ } - result = (int) len; + tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len); + result->payload_len = len + AUTH_CELL_HEADER_LEN; + set_uint16(result->payload+2, htons(len)); + goto done; err: - result = -1; + var_cell_free(result); + result = NULL; done: auth1_free(auth); auth_ctx_free(ctx); @@ -2379,44 +2618,29 @@ 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(); - int authlen; - size_t cell_maxlen; /* XXXX make sure we're actually supposed to send this! */ if (!pk) { log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key"); return -1; } - if (authtype != AUTHTYPE_RSA_SHA256_TLSSECRET) { + if (! authchallenge_type_is_supported(authtype)) { log_warn(LD_BUG, "Tried to send authenticate cell with unknown " "authentication type %d", authtype); return -1; } - cell_maxlen = 4 + /* overhead */ - V3_AUTH_BODY_LEN + /* Authentication body */ - crypto_pk_keysize(pk) + /* Max signature length */ - 16 /* add a few extra bytes just in case. */; - - cell = var_cell_new(cell_maxlen); - cell->command = CELL_AUTHENTICATE; - set_uint16(cell->payload, htons(AUTHTYPE_RSA_SHA256_TLSSECRET)); - /* skip over length ; we don't know that yet. */ - - authlen = connection_or_compute_authenticate_cell_body(conn, - cell->payload+4, - cell_maxlen-4, - pk, - 0 /* not server */); - if (authlen < 0) { + cell = connection_or_compute_authenticate_cell_body(conn, + authtype, + pk, + get_current_auth_keypair(), + 0 /* not server */); + if (! cell) { + /* LCOV_EXCL_START */ log_warn(LD_BUG, "Unable to compute authenticate cell!"); - var_cell_free(cell); return -1; + /* LCOV_EXCL_STOP */ } - tor_assert(authlen + 4 <= cell->payload_len); - set_uint16(cell->payload+2, htons(authlen)); - cell->payload_len = authlen + 4; - connection_or_write_var_cell_to_buf(cell, conn); var_cell_free(cell); diff --git a/src/or/connection_or.h b/src/or/connection_or.h index 2e8c6066cc..da95718ac9 100644 --- a/src/or/connection_or.h +++ b/src/or/connection_or.h @@ -40,7 +40,9 @@ void connection_or_notify_error(or_connection_t *conn, MOCK_DECL(or_connection_t *, connection_or_connect, (const tor_addr_t *addr, uint16_t port, - const char *id_digest, channel_tls_t *chan)); + const char *id_digest, + const ed25519_public_key_t *ed_id, + channel_tls_t *chan)); void connection_or_close_normally(or_connection_t *orconn, int flush); MOCK_DECL(void,connection_or_close_for_error, @@ -59,10 +61,12 @@ int connection_init_or_handshake_state(or_connection_t *conn, void connection_or_init_conn_from_address(or_connection_t *conn, const tor_addr_t *addr, uint16_t port, - const char *id_digest, + const char *rsa_id_digest, + const ed25519_public_key_t *ed_id, int started_here); int connection_or_client_learned_peer_id(or_connection_t *conn, - const uint8_t *peer_id); + const uint8_t *rsa_peer_id, + const ed25519_public_key_t *ed_peer_id); time_t connection_or_client_used(or_connection_t *conn); MOCK_DECL(int, connection_or_get_num_circuits, (or_connection_t *conn)); void or_handshake_state_free(or_handshake_state_t *state); @@ -84,10 +88,14 @@ int connection_or_send_versions(or_connection_t *conn, int v3_plus); 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 authchallenge_type_is_supported(uint16_t challenge_type); +int authchallenge_type_is_better(uint16_t challenge_type_a, + uint16_t challenge_type_b); +var_cell_t *connection_or_compute_authenticate_cell_body(or_connection_t *conn, + const int authtype, + crypto_pk_t *signing_key, + const ed25519_keypair_t *ed_signing_key, + int server); MOCK_DECL(int,connection_or_send_authenticate_cell, (or_connection_t *conn, int type)); diff --git a/src/or/control.c b/src/or/control.c index 1cc2e1464e..96cc41bc4b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -5,7 +5,31 @@ /** * \file control.c * \brief Implementation for Tor's control-socket interface. - * See doc/spec/control-spec.txt for full details on protocol. + * + * A "controller" is an external program that monitors and controls a Tor + * instance via a text-based protocol. It connects to Tor via a connection + * to a local socket. + * + * The protocol is line-driven. The controller sends commands terminated by a + * CRLF. Tor sends lines that are either <em>replies</em> to what the + * controller has said, or <em>events</em> that Tor sends to the controller + * asynchronously based on occurrences in the Tor network model. + * + * See the control-spec.txt file in the torspec.git repository for full + * details on protocol. + * + * This module generally has two kinds of entry points: those based on having + * received a command on a controller socket, which are handled in + * connection_control_process_inbuf(), and dispatched to individual functions + * with names like control_handle_COMMANDNAME(); and those based on events + * that occur elsewhere in Tor, which are handled by functions with names like + * control_event_EVENTTYPE(). + * + * Controller events are not sent immediately; rather, they are inserted into + * the queued_control_events array, and flushed later from + * flush_queued_events_cb(). Doing this simplifies our callgraph greatly, + * by limiting the number of places in Tor that can call back into the network + * stack. **/ #define CONTROL_PRIVATE @@ -919,7 +943,7 @@ control_setconf_helper(control_connection_t *conn, uint32_t len, char *body, ++body; } - smartlist_add(entries, tor_strdup("")); + smartlist_add_strdup(entries, ""); config = smartlist_join_strings(entries, "\n", 0, NULL); SMARTLIST_FOREACH(entries, char *, cp, tor_free(cp)); smartlist_free(entries); @@ -3116,7 +3140,7 @@ handle_control_getinfo(control_connection_t *conn, uint32_t len, if (!ans) { smartlist_add(unrecognized, (char*)q); } else { - smartlist_add(answers, tor_strdup(q)); + smartlist_add_strdup(answers, q); smartlist_add(answers, ans); } } SMARTLIST_FOREACH_END(q); @@ -4058,7 +4082,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, * of the id. */ desc_id = digest; } else { - connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n", + connection_printf_to_buf(conn, "513 Invalid argument \"%s\"\r\n", arg1); goto done; } @@ -4250,6 +4274,8 @@ handle_control_add_onion(control_connection_t *conn, int max_streams = 0; int max_streams_close_circuit = 0; rend_auth_type_t auth_type = REND_NO_AUTH; + /* Default to adding an anonymous hidden service if no flag is given */ + int non_anonymous = 0; for (size_t i = 1; i < arg_len; i++) { static const char *port_prefix = "Port="; static const char *flags_prefix = "Flags="; @@ -4286,11 +4312,16 @@ handle_control_add_onion(control_connection_t *conn, * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is * exceeded. * * 'BasicAuth' - Client authorization using the 'basic' method. + * * 'NonAnonymous' - Add a non-anonymous Single Onion Service. If this + * flag is present, tor must be in non-anonymous + * hidden service mode. If this flag is absent, + * tor must be in anonymous hidden service mode. */ static const char *discard_flag = "DiscardPK"; static const char *detach_flag = "Detach"; static const char *max_s_close_flag = "MaxStreamsCloseCircuit"; static const char *basicauth_flag = "BasicAuth"; + static const char *non_anonymous_flag = "NonAnonymous"; smartlist_t *flags = smartlist_new(); int bad = 0; @@ -4311,6 +4342,8 @@ handle_control_add_onion(control_connection_t *conn, max_streams_close_circuit = 1; } else if (!strcasecmp(flag, basicauth_flag)) { auth_type = REND_BASIC_AUTH; + } else if (!strcasecmp(flag, non_anonymous_flag)) { + non_anonymous = 1; } else { connection_printf_to_buf(conn, "512 Invalid 'Flags' argument: %s\r\n", @@ -4379,6 +4412,19 @@ handle_control_add_onion(control_connection_t *conn, smartlist_len(auth_clients) > 16)) { connection_printf_to_buf(conn, "512 Too many auth clients\r\n"); goto out; + } else if (non_anonymous != rend_service_non_anonymous_mode_enabled( + get_options())) { + /* If we failed, and the non-anonymous flag is set, Tor must be in + * anonymous hidden service mode. + * The error message changes based on the current Tor config: + * 512 Tor is in anonymous hidden service mode + * 512 Tor is in non-anonymous hidden service mode + * (I've deliberately written them out in full here to aid searchability.) + */ + connection_printf_to_buf(conn, "512 Tor is in %sanonymous hidden service " + "mode\r\n", + non_anonymous ? "" : "non-"); + goto out; } /* Parse the "keytype:keyblob" argument. */ @@ -6000,9 +6046,9 @@ control_event_networkstatus_changed_helper(smartlist_t *statuses, return 0; strs = smartlist_new(); - smartlist_add(strs, tor_strdup("650+")); - smartlist_add(strs, tor_strdup(event_string)); - smartlist_add(strs, tor_strdup("\r\n")); + smartlist_add_strdup(strs, "650+"); + smartlist_add_strdup(strs, event_string); + smartlist_add_strdup(strs, "\r\n"); SMARTLIST_FOREACH(statuses, const routerstatus_t *, rs, { s = networkstatus_getinfo_helper_single(rs); diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index 2e76ea5b78..fd6de6ea7c 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -8,7 +8,11 @@ * \brief Uses the workqueue/threadpool code to farm CPU-intensive activities * out to subprocesses. * - * Right now, we only use this for processing onionskins. + * The multithreading backend for this module is in workqueue.c; this module + * specializes workqueue.c. + * + * Right now, we only use this for processing onionskins, and invoke it mostly + * from onion.c. **/ #include "or.h" #include "channel.h" @@ -164,6 +168,7 @@ update_state_threadfn(void *state_, void *work_) server_onion_keys_free(state->onion_keys); state->onion_keys = update->onion_keys; update->onion_keys = NULL; + worker_state_free(update); ++state->generation; return WQ_RPL_REPLY; } diff --git a/src/or/dircollate.c b/src/or/dircollate.c index 756011b934..033a7afe0f 100644 --- a/src/or/dircollate.c +++ b/src/or/dircollate.c @@ -8,6 +8,17 @@ * * \brief Collation code for figuring out which identities to vote for in * the directory voting process. + * + * During the consensus calculation, when an authority is looking at the vote + * documents from all the authorities, it needs to compute the consensus for + * each relay listed by at least one authority. But the notion of "each + * relay" can be tricky: some relays have Ed25519 keys, and others don't. + * + * Moreover, older consensus methods did RSA-based ID collation alone, and + * ignored Ed25519 keys. We need to support those too until we're completely + * sure that authorities will never downgrade. + * + * This module is invoked exclusively from dirvote.c. */ #define DIRCOLLATE_PRIVATE @@ -21,6 +32,9 @@ static void dircollator_collate_by_ed25519(dircollator_t *dc); * RSA SHA1 digest) to an array of vote_routerstatus_t. */ typedef struct ddmap_entry_s { HT_ENTRY(ddmap_entry_s) node; + /** A SHA1-RSA1024 identity digest and Ed25519 identity key, + * concatenated. (If there is no ed25519 identity key, there is no + * entry in this table.) */ 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. */ @@ -43,12 +57,16 @@ ddmap_entry_new(int n_votes) sizeof(vote_routerstatus_t *) * n_votes); } +/** Helper: compute a hash of a single ddmap_entry_t's identity (or + * identities) */ static unsigned ddmap_entry_hash(const ddmap_entry_t *ent) { return (unsigned) siphash24g(ent->d, sizeof(ent->d)); } +/** Helper: return true if <b>a</b> and <b>b</b> have the same + * identity/identities. */ static unsigned ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b) { @@ -56,7 +74,7 @@ ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b) } /** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the - * ed25519 identity as <b>ed25519</b>. */ + * ed25519 identity as <b>ed25519</b>. Both must be provided. */ static void ddmap_entry_set_digests(ddmap_entry_t *ent, const uint8_t *rsa_sha1, @@ -72,8 +90,12 @@ 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. */ + * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of its RSA + * key digest and Ed25519 key. It must come from the <b>vote_num</b>th + * vote. + * + * Requires that the vote is well-formed -- that is, that it has no duplicate + * routerstatus entries. We already checked for that when parsing the vote. */ static void dircollator_add_routerstatus(dircollator_t *dc, int vote_num, @@ -82,12 +104,15 @@ dircollator_add_routerstatus(dircollator_t *dc, { const char *id = vrs->status.identity_digest; + /* Clear this flag; we might set it later during the voting process */ vrs->ed25519_reflects_consensus = 0; - (void) vote; + (void) vote; // We don't currently need this. + + /* First, add this item to the appropriate RSA-SHA-Id array. */ 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); + vrs_lst = tor_calloc(dc->n_votes, sizeof(vote_routerstatus_t *)); digestmap_set(dc->by_rsa_sha1, id, vrs_lst); } tor_assert(vrs_lst[vote_num] == NULL); @@ -98,6 +123,7 @@ dircollator_add_routerstatus(dircollator_t *dc, if (! vrs->has_ed25519_listing) return; + /* Now add it to the appropriate <Ed,RSA-SHA-Id> array. */ ddmap_entry_t search, *found; memset(&search, 0, sizeof(search)); ddmap_entry_set_digests(&search, (const uint8_t *)id, ed); diff --git a/src/or/directory.c b/src/or/directory.c index 75fc103577..ba6d38c426 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -44,9 +44,38 @@ /** * \file directory.c - * \brief Code to send and fetch directories and router - * descriptors via HTTP. Directories use dirserv.c to generate the - * results; clients use routers.c to parse them. + * \brief Code to send and fetch information from directory authorities and + * caches via HTTP. + * + * Directory caches and authorities use dirserv.c to generate the results of a + * query and stream them to the connection; clients use routerparse.c to parse + * them. + * + * Every directory request has a dir_connection_t on the client side and on + * the server side. In most cases, the dir_connection_t object is a linked + * connection, tunneled through an edge_connection_t so that it can be a + * stream on the Tor network. The only non-tunneled connections are those + * that are used to upload material (descriptors and votes) to authorities. + * Among tunneled connections, some use one-hop circuits, and others use + * multi-hop circuits for anonymity. + * + * Directory requests are launched by calling + * directory_initiate_command_rend() or one of its numerous variants. This + * launch the connection, will construct an HTTP request with + * directory_send_command(), send the and wait for a response. The client + * later handles the response with connection_dir_client_reached_eof(), + * which passes the information received to another part of Tor. + * + * On the server side, requests are read in directory_handle_command(), + * which dispatches first on the request type (GET or POST), and then on + * the URL requested. GET requests are processed with a table-based + * dispatcher in url_table[]. The process of handling larger GET requests + * is complicated because we need to avoid allocating a copy of all the + * data to be sent to the client in one huge buffer. Instead, we spool the + * data into the buffer using logic in connection_dirserv_flushed_some() in + * dirserv.c. (TODO: If we extended buf.c to have a zero-copy + * reference-based buffer type, we could remove most of that code, at the + * cost of a bit more reference counting.) **/ /* In-points to directory.c: @@ -124,29 +153,55 @@ static void connection_dir_close_consensus_fetches( /********* END VARIABLES ************/ -/** Return true iff the directory purpose <b>dir_purpose</b> (and if it's - * fetching descriptors, it's fetching them for <b>router_purpose</b>) - * must use an anonymous connection to a directory. */ +/** Return false if the directory purpose <b>dir_purpose</b> + * does not require an anonymous (three-hop) connection. + * + * Return true 1) by default, 2) if all directory actions have + * specifically been configured to be over an anonymous connection, + * or 3) if the router is a bridge */ int -purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose) +purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose, + const char *resource) { if (get_options()->AllDirActionsPrivate) return 1; - if (router_purpose == ROUTER_PURPOSE_BRIDGE) + + if (router_purpose == ROUTER_PURPOSE_BRIDGE) { + if (dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC + && resource && !strcmp(resource, "authority.z")) { + /* We are asking a bridge for its own descriptor. That doesn't need + anonymity. */ + return 0; + } + /* Assume all other bridge stuff needs anonymity. */ return 1; /* if no circuits yet, this might break bootstrapping, but it's * needed to be safe. */ - if (dir_purpose == DIR_PURPOSE_UPLOAD_DIR || - dir_purpose == DIR_PURPOSE_UPLOAD_VOTE || - dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES || - dir_purpose == DIR_PURPOSE_FETCH_STATUS_VOTE || - dir_purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES || - dir_purpose == DIR_PURPOSE_FETCH_CONSENSUS || - dir_purpose == DIR_PURPOSE_FETCH_CERTIFICATE || - dir_purpose == DIR_PURPOSE_FETCH_SERVERDESC || - dir_purpose == DIR_PURPOSE_FETCH_EXTRAINFO || - dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) - return 0; - return 1; + } + + switch (dir_purpose) + { + case DIR_PURPOSE_UPLOAD_DIR: + case DIR_PURPOSE_UPLOAD_VOTE: + case DIR_PURPOSE_UPLOAD_SIGNATURES: + case DIR_PURPOSE_FETCH_STATUS_VOTE: + case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES: + case DIR_PURPOSE_FETCH_CONSENSUS: + case DIR_PURPOSE_FETCH_CERTIFICATE: + case DIR_PURPOSE_FETCH_SERVERDESC: + case DIR_PURPOSE_FETCH_EXTRAINFO: + case DIR_PURPOSE_FETCH_MICRODESC: + return 0; + case DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2: + case DIR_PURPOSE_UPLOAD_RENDDESC_V2: + case DIR_PURPOSE_FETCH_RENDDESC_V2: + return 1; + case DIR_PURPOSE_SERVER: + default: + log_warn(LD_BUG, "Called with dir_purpose=%d, router_purpose=%d", + dir_purpose, router_purpose); + tor_assert_nonfatal_unreached(); + return 1; /* Assume it needs anonymity; better safe than sorry. */ + } } /** Return a newly allocated string describing <b>auth</b>. Only describes @@ -351,7 +406,7 @@ 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); } - if (purpose_needs_anonymity(dir_purpose, router_purpose)) { + if (purpose_needs_anonymity(dir_purpose, router_purpose, NULL)) { indirection = DIRIND_ANONYMOUS; } else if (!fascist_firewall_allows_dir_server(ds, FIREWALL_DIR_CONNECTION, @@ -445,7 +500,8 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( 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); + int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose, + resource); dirinfo_type_t type = dir_fetch_type(dir_purpose, router_purpose, resource); time_t if_modified_since = 0; @@ -499,9 +555,6 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( * sort of dir fetch we'll be doing, so it won't return a bridge * that can't answer our question. */ - /* XXX+++++ Not all bridges handle conditional consensus downloading, - * so, for now, never assume the server supports that. -PP - * Is that assumption still so in 2016? -NM */ const node_t *node = choose_random_dirguard(type); if (node && node->ri) { /* every bridge has a routerinfo. */ @@ -582,7 +635,7 @@ MOCK_IMPL(void, directory_get_from_dirserver, ( "While fetching directory info, " "no running dirservers known. Will try again later. " "(purpose %d)", dir_purpose); - if (!purpose_needs_anonymity(dir_purpose, router_purpose)) { + if (!purpose_needs_anonymity(dir_purpose, router_purpose, resource)) { /* remember we tried them all and failed. */ directory_all_unreachable(time(NULL)); } @@ -1085,18 +1138,6 @@ directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port, if_modified_since, NULL); } -/** Return non-zero iff a directory connection with purpose - * <b>dir_purpose</b> reveals sensitive information about a Tor - * instance's client activities. (Such connections must be performed - * through normal three-hop Tor circuits.) */ -static int -is_sensitive_dir_purpose(uint8_t dir_purpose) -{ - return ((dir_purpose == DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2) || - (dir_purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) || - (dir_purpose == DIR_PURPOSE_FETCH_RENDDESC_V2)); -} - /** Same as directory_initiate_command(), but accepts rendezvous data to * fetch a hidden service descriptor, and takes its address & port arguments * as tor_addr_port_t. */ @@ -1144,12 +1185,10 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port, log_debug(LD_DIR, "Initiating %s", dir_conn_purpose_to_string(dir_purpose)); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(is_sensitive_dir_purpose(dir_purpose) && - !anonymized_connection)); -#else - (void)is_sensitive_dir_purpose; -#endif + if (purpose_needs_anonymity(dir_purpose, router_purpose, resource)) { + tor_assert(anonymized_connection || + rend_non_anonymous_mode_enabled(options)); + } /* use encrypted begindir connections for everything except relays * this provides better protection for directory fetches */ @@ -1311,9 +1350,9 @@ compare_strs_(const void **a, const void **b) /** Return the URL we should use for a consensus download. * - * This url depends on whether or not the server we go to - * is sufficiently new to support conditional consensus downloading, - * i.e. GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b> + * Use the "conditional consensus downloading" feature described in + * dir-spec.txt, i.e. + * GET .../consensus/<b>fpr</b>+<b>fpr</b>+<b>fpr</b> * * If 'resource' is provided, it is the name of a consensus flavor to request. */ diff --git a/src/or/directory.h b/src/or/directory.h index 210d6238b9..acb7394136 100644 --- a/src/or/directory.h +++ b/src/or/directory.h @@ -132,7 +132,8 @@ 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); -int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose); +int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose, + const char *resource); #ifdef DIRECTORY_PRIVATE diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 64ebde6fdd..c797c9aa0e 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -24,6 +24,7 @@ #include "networkstatus.h" #include "nodelist.h" #include "policies.h" +#include "protover.h" #include "rephist.h" #include "router.h" #include "routerlist.h" @@ -35,6 +36,24 @@ * \file dirserv.c * \brief Directory server core implementation. Manages directory * contents and generates directories. + * + * This module implements most of directory cache functionality, and some of + * the directory authority functionality. The directory.c module delegates + * here in order to handle incoming requests from clients, via + * connection_dirserv_flushed_some() and its kin. In order to save RAM, this + * module is reponsible for spooling directory objects (in whole or in part) + * onto buf_t instances, and then closing the dir_connection_t once the + * objects are totally flushed. + * + * The directory.c module also delegates here for handling descriptor uploads + * via dirserv_add_multiple_descriptors(). + * + * Additionally, this module handles some aspects of voting, including: + * deciding how to vote on individual flags (based on decisions reached in + * rephist.c), of formatting routerstatus lines, and deciding what relays to + * include in an authority's vote. (TODO: Those functions could profitably be + * split off. They only live in this file because historically they were + * shared among the v1, v2, and v3 directory code.) */ /** How far in the future do we allow a router to get? (seconds) */ @@ -255,6 +274,20 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, return FP_REJECT; } + /* dirserv_get_status_impl already rejects versions older than 0.2.4.18-rc, + * and onion_curve25519_pkey was introduced in 0.2.4.8-alpha. + * But just in case a relay doesn't provide or lies about its version, or + * doesn't include an ntor key in its descriptor, check that it exists, + * and is non-zero (clients check that it's non-zero before using it). */ + if (!routerinfo_has_curve25519_onion_key(router)) { + log_fn(severity, LD_DIR, + "Descriptor from router %s is missing an ntor curve25519 onion " + "key.", router_describe(router)); + if (msg) + *msg = "Missing ntor curve25519 onion key. Please upgrade!"; + return FP_REJECT; + } + if (router->cache_info.signing_key_cert) { /* This has an ed25519 identity key. */ if (KEYPIN_MISMATCH == @@ -915,7 +948,7 @@ list_server_status_v1(smartlist_t *routers, char **router_status_out, if (!node->is_running) *cp++ = '!'; router_get_verbose_nickname(cp, ri); - smartlist_add(rs_entries, tor_strdup(name_buf)); + smartlist_add_strdup(rs_entries, name_buf); } else if (ri->cache_info.published_on >= cutoff) { smartlist_add(rs_entries, list_single_server_status(ri, node->is_running)); @@ -1781,6 +1814,7 @@ version_from_platform(const char *platform) */ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, + const char *protocols, routerstatus_format_type_t format, const vote_routerstatus_t *vrs) { @@ -1844,6 +1878,9 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) { smartlist_add_asprintf(chunks, "v %s\n", version); } + if (protocols) { + smartlist_add_asprintf(chunks, "pr %s\n", protocols); + } if (format != NS_V2) { const routerinfo_t* desc = router_get_by_id_digest(rs->identity_digest); @@ -1911,7 +1948,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, vrs->status.guardfraction_percentage); } - smartlist_add(chunks, tor_strdup("\n")); + smartlist_add_strdup(chunks, "\n"); if (desc) { summary = policy_summarize(desc->exit_policy, AF_INET); @@ -1921,7 +1958,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, 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")); + smartlist_add_strdup(chunks, "id ed25519 none\n"); } else { char ed_b64[BASE64_DIGEST256_LEN+1]; digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id); @@ -2822,6 +2859,12 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, rs->is_flagged_running = 0; vrs->version = version_from_platform(ri->platform); + if (ri->protocol_list) { + vrs->protocols = tor_strdup(ri->protocol_list); + } else { + vrs->protocols = tor_strdup( + protover_compute_for_old_tor(vrs->version)); + } vrs->microdesc = dirvote_format_all_microdesc_vote_lines(ri, now, microdescriptors); @@ -2894,12 +2937,37 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, v3_out->client_versions = client_versions; v3_out->server_versions = server_versions; + + /* These are hardwired, to avoid disaster. */ + v3_out->recommended_relay_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->recommended_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_client_protocols = + tor_strdup("Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=4 LinkAuth=1 Microdesc=1-2 Relay=2"); + v3_out->required_relay_protocols = + tor_strdup("Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=3-4 LinkAuth=1 Microdesc=1 Relay=1-2"); + + /* We are not allowed to vote to require anything we don't have. */ + tor_assert(protover_all_supported(v3_out->required_relay_protocols, NULL)); + tor_assert(protover_all_supported(v3_out->required_client_protocols, NULL)); + + /* We should not recommend anything we don't have. */ + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_relay_protocols, NULL)); + tor_assert_nonfatal(protover_all_supported( + v3_out->recommended_client_protocols, NULL)); + v3_out->package_lines = smartlist_new(); { config_line_t *cl; for (cl = get_options()->RecommendedPackages; cl; cl = cl->next) { if (validate_recommended_package_line(cl->value)) - smartlist_add(v3_out->package_lines, tor_strdup(cl->value)); + smartlist_add_strdup(v3_out->package_lines, cl->value); } } @@ -2908,9 +2976,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, "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")); + smartlist_add_strdup(v3_out->known_flags, "Running"); if (listbadexits) - smartlist_add(v3_out->known_flags, tor_strdup("BadExit")); + smartlist_add_strdup(v3_out->known_flags, "BadExit"); smartlist_sort_strings(v3_out->known_flags); if (options->ConsensusParams) { @@ -3185,7 +3253,9 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) router->nickname, fmt_addr32(router->addr), router->or_port); tor_addr_from_ipv4h(&router_addr, router->addr); chan = channel_tls_connect(&router_addr, router->or_port, - router->cache_info.identity_digest); + router->cache_info.identity_digest, + NULL // XXXX Ed25519 ID. + ); if (chan) command_setup_channel(chan); /* Possible IPv6. */ @@ -3197,7 +3267,9 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) tor_addr_to_str(addrstr, &router->ipv6_addr, sizeof(addrstr), 1), router->ipv6_orport); chan = channel_tls_connect(&router->ipv6_addr, router->ipv6_orport, - router->cache_info.identity_digest); + router->cache_info.identity_digest, + NULL // XXXX Ed25519 ID. + ); if (chan) command_setup_channel(chan); } } diff --git a/src/or/dirserv.h b/src/or/dirserv.h index 3c914e9311..1e4f27e3d7 100644 --- a/src/or/dirserv.h +++ b/src/or/dirserv.h @@ -96,7 +96,9 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs, size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed); char *routerstatus_format_entry( - const routerstatus_t *rs, const char *platform, + const routerstatus_t *rs, + const char *version, + const char *protocols, routerstatus_format_type_t format, const vote_routerstatus_t *vrs); void dirserv_free_all(void); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 9748f4ae4d..d14af41667 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -13,6 +13,7 @@ #include "microdesc.h" #include "networkstatus.h" #include "policies.h" +#include "protover.h" #include "rephist.h" #include "router.h" #include "routerkeys.h" @@ -25,6 +26,39 @@ /** * \file dirvote.c * \brief Functions to compute directory consensus, and schedule voting. + * + * This module is the center of the consensus-voting based directory + * authority system. With this system, a set of authorities first + * publish vote based on their opinions of the network, and then compute + * a consensus from those votes. Each authority signs the consensus, + * and clients trust the consensus if enough known authorities have + * signed it. + * + * The code in this module is only invoked on directory authorities. It's + * responsible for: + * + * <ul> + * <li>Generating this authority's vote networkstatus, based on the + * authority's view of the network as represented in dirserv.c + * <li>Formatting the vote networkstatus objects. + * <li>Generating the microdescriptors that correspond to our own + * vote. + * <li>Sending votes to all the other authorities. + * <li>Trying to fetch missing votes from other authorities. + * <li>Computing the consensus from a set of votes, as well as + * a "detached signature" object for other authorities to fetch. + * <li>Collecting other authorities' signatures on the same consensus, + * until there are enough. + * <li>Publishing the consensus to the reset of the directory system. + * <li>Scheduling all of the above operations. + * </ul> + * + * The main entry points are in dirvote_act(), which handles scheduled + * actions; and dirvote_add_vote() and dirvote_add_signatures(), which + * handle uploaded and downloaded votes and signatures. + * + * (See dir-spec.txt from torspec.git for a complete specification of + * the directory protocol and voting algorithms.) **/ /** A consensus that we have built and are appending signatures to. Once it's @@ -61,6 +95,58 @@ static int dirvote_publish_consensus(void); * Voting * =====*/ +/* If <b>opt_value</b> is non-NULL, return "keyword opt_value\n" in a new + * string. Otherwise return a new empty string. */ +static char * +format_line_if_present(const char *keyword, const char *opt_value) +{ + if (opt_value) { + char *result = NULL; + tor_asprintf(&result, "%s %s\n", keyword, opt_value); + return result; + } else { + return tor_strdup(""); + } +} + +/** Format the recommended/required-relay-client protocols lines for a vote in + * a newly allocated string, and return that string. */ +static char * +format_protocols_lines_for_vote(const networkstatus_t *v3_ns) +{ + char *recommended_relay_protocols_line = NULL; + char *recommended_client_protocols_line = NULL; + char *required_relay_protocols_line = NULL; + char *required_client_protocols_line = NULL; + + recommended_relay_protocols_line = + format_line_if_present("recommended-relay-protocols", + v3_ns->recommended_relay_protocols); + recommended_client_protocols_line = + format_line_if_present("recommended-client-protocols", + v3_ns->recommended_client_protocols); + required_relay_protocols_line = + format_line_if_present("required-relay-protocols", + v3_ns->required_relay_protocols); + required_client_protocols_line = + format_line_if_present("required-client-protocols", + v3_ns->required_client_protocols); + + char *result = NULL; + tor_asprintf(&result, "%s%s%s%s", + recommended_relay_protocols_line, + recommended_client_protocols_line, + required_relay_protocols_line, + required_client_protocols_line); + + tor_free(recommended_relay_protocols_line); + tor_free(recommended_client_protocols_line); + tor_free(required_relay_protocols_line); + tor_free(required_client_protocols_line); + + return result; +} + /** Return a new string containing the string representation of the vote in * <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>. * For v3 authorities. */ @@ -69,11 +155,11 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, networkstatus_t *v3_ns) { smartlist_t *chunks = smartlist_new(); - const char *client_versions = NULL, *server_versions = NULL; char *packages = NULL; char fingerprint[FINGERPRINT_LEN+1]; char digest[DIGEST_LEN]; uint32_t addr; + char *protocols_lines = NULL; char *client_versions_line = NULL, *server_versions_line = NULL; char *shared_random_vote_str = NULL; networkstatus_voter_info_t *voter; @@ -88,21 +174,12 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, base16_encode(fingerprint, sizeof(fingerprint), v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); - client_versions = v3_ns->client_versions; - server_versions = v3_ns->server_versions; - if (client_versions) { - tor_asprintf(&client_versions_line, "client-versions %s\n", - client_versions); - } else { - client_versions_line = tor_strdup(""); - } - if (server_versions) { - tor_asprintf(&server_versions_line, "server-versions %s\n", - server_versions); - } else { - server_versions_line = tor_strdup(""); - } + client_versions_line = format_line_if_present("client-versions", + v3_ns->client_versions); + server_versions_line = format_line_if_present("server-versions", + v3_ns->server_versions); + protocols_lines = format_protocols_lines_for_vote(v3_ns); if (v3_ns->package_lines) { smartlist_t *tmp = smartlist_new(); @@ -154,6 +231,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, "valid-until %s\n" "voting-delay %d %d\n" "%s%s" /* versions */ + "%s" /* protocols */ "%s" /* packages */ "known-flags %s\n" "flag-thresholds %s\n" @@ -167,6 +245,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, v3_ns->vote_seconds, v3_ns->dist_seconds, client_versions_line, server_versions_line, + protocols_lines, packages, flags, flag_thresholds, @@ -198,16 +277,17 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, char *rsf; vote_microdesc_hash_t *h; rsf = routerstatus_format_entry(&vrs->status, - vrs->version, NS_V3_VOTE, vrs); + vrs->version, vrs->protocols, + NS_V3_VOTE, vrs); if (rsf) smartlist_add(chunks, rsf); for (h = vrs->microdesc; h; h = h->next) { - smartlist_add(chunks, tor_strdup(h->microdesc_hash_line)); + smartlist_add_strdup(chunks, h->microdesc_hash_line); } } SMARTLIST_FOREACH_END(vrs); - smartlist_add(chunks, tor_strdup("directory-footer\n")); + smartlist_add_strdup(chunks, "directory-footer\n"); /* The digest includes everything up through the space after * directory-signature. (Yuck.) */ @@ -258,6 +338,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, done: tor_free(client_versions_line); tor_free(server_versions_line); + tor_free(protocols_lines); tor_free(packages); SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); @@ -832,7 +913,7 @@ networkstatus_check_weights(int64_t Wgg, int64_t Wgd, int64_t Wmg, * * It returns true if weights could be computed, false otherwise. */ -static int +int networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, int64_t M, int64_t E, int64_t D, int64_t T, int64_t weight_scale) @@ -853,7 +934,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, } /* - * Computed from cases in 3.4.3 of dir-spec.txt + * Computed from cases in 3.8.3 of dir-spec.txt * * 1. Neither are scarce * 2. Both Guard and Exit are scarce @@ -914,7 +995,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, Wgd = weight_scale; } } else { // Subcase b: R+D >= S - casename = "Case 2b1 (Wgg=1, Wmd=Wgd)"; + casename = "Case 2b1 (Wgg=weight_scale, Wmd=Wgd)"; Wee = (weight_scale*(E - G + M))/E; Wed = (weight_scale*(D - 2*E + 4*G - 2*M))/(3*D); Wme = (weight_scale*(G-M))/E; @@ -927,7 +1008,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, weight_scale, G, M, E, D, T, 10, 1); if (berr) { - casename = "Case 2b2 (Wgg=1, Wee=1)"; + casename = "Case 2b2 (Wgg=weight_scale, Wee=weight_scale)"; Wgg = weight_scale; Wee = weight_scale; Wed = (weight_scale*(D - 2*E + G + M))/(3*D); @@ -996,7 +1077,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, } else { // Subcase b: S+D >= T/3 // D != 0 because S+D >= T/3 if (G < E) { - casename = "Case 3bg (G scarce, Wgg=1, Wmd == Wed)"; + casename = "Case 3bg (G scarce, Wgg=weight_scale, Wmd == Wed)"; Wgg = weight_scale; Wgd = (weight_scale*(D - 2*G + E + M))/(3*D); Wmg = 0; @@ -1008,7 +1089,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, berr = networkstatus_check_weights(Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed, weight_scale, G, M, E, D, T, 10, 1); } else { // G >= E - casename = "Case 3be (E scarce, Wee=1, Wmd == Wgd)"; + casename = "Case 3be (E scarce, Wee=weight_scale, Wmd == Wgd)"; Wee = weight_scale; Wed = (weight_scale*(D - 2*E + G + M))/(3*D); Wme = 0; @@ -1042,7 +1123,7 @@ networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, tor_assert(0 < weight_scale && weight_scale <= INT32_MAX); /* - * Provide Wgm=Wgg, Wmm=1, Wem=Wee, Weg=Wed. May later determine + * Provide Wgm=Wgg, Wmm=weight_scale, Wem=Wee, Weg=Wed. May later determine * that middle nodes need different bandwidth weights for dirport traffic, * or that weird exit policies need special weight, or that bridges * need special weight. @@ -1152,6 +1233,72 @@ update_total_bandwidth_weights(const routerstatus_t *rs, } } +/** Considering the different recommended/required protocols sets as a + * 4-element array, return the element from <b>vote</b> for that protocol + * set. + */ +static const char * +get_nth_protocol_set_vote(int n, const networkstatus_t *vote) +{ + switch (n) { + case 0: return vote->recommended_client_protocols; + case 1: return vote->recommended_relay_protocols; + case 2: return vote->required_client_protocols; + case 3: return vote->required_relay_protocols; + default: + tor_assert_unreached(); + return NULL; + } +} + +/** Considering the different recommended/required protocols sets as a + * 4-element array, return a newly allocated string for the consensus value + * for the n'th set. + */ +static char * +compute_nth_protocol_set(int n, int n_voters, const smartlist_t *votes) +{ + const char *keyword; + smartlist_t *proto_votes = smartlist_new(); + int threshold; + switch (n) { + case 0: + keyword = "recommended-client-protocols"; + threshold = CEIL_DIV(n_voters, 2); + break; + case 1: + keyword = "recommended-relay-protocols"; + threshold = CEIL_DIV(n_voters, 2); + break; + case 2: + keyword = "required-client-protocols"; + threshold = CEIL_DIV(n_voters * 2, 3); + break; + case 3: + keyword = "required-relay-protocols"; + threshold = CEIL_DIV(n_voters * 2, 3); + break; + default: + tor_assert_unreached(); + return NULL; + } + + SMARTLIST_FOREACH_BEGIN(votes, const networkstatus_t *, ns) { + const char *v = get_nth_protocol_set_vote(n, ns); + if (v) + smartlist_add(proto_votes, (void*)v); + } SMARTLIST_FOREACH_END(ns); + + char *protocols = protover_compute_vote(proto_votes, threshold); + smartlist_free(proto_votes); + + char *result = NULL; + tor_asprintf(&result, "%s %s\n", keyword, protocols); + tor_free(protocols); + + return result; +} + /** Given a list of vote networkstatus_t in <b>votes</b>, our public * authority <b>identity_key</b>, our private authority <b>signing_key</b>, * and the number of <b>total_authorities</b> that we believe exist in our @@ -1159,7 +1306,17 @@ update_total_bandwidth_weights(const routerstatus_t *rs, * value in a newly allocated string. * * Note: this function DOES NOT check whether the votes are from - * recognized authorities. (dirvote_add_vote does that.) */ + * recognized authorities. (dirvote_add_vote does that.) + * + * <strong>WATCH OUT</strong>: You need to think before you change the + * behavior of this function, or of the functions it calls! If some + * authorities compute the consensus with a different algorithm than + * others, they will not reach the same result, and they will not all + * sign the same thing! If you really need to change the algorithm + * here, you should allocate a new "consensus_method" for the new + * behavior, and make the new behavior conditional on a new-enough + * consensus_method. + **/ char * networkstatus_compute_consensus(smartlist_t *votes, int total_authorities, @@ -1178,7 +1335,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_t *flags; const char *flavor_name; uint32_t max_unmeasured_bw_kb = DEFAULT_MAX_UNMEASURED_BW_KB; - int64_t G=0, M=0, E=0, D=0, T=0; /* For bandwidth weights */ + int64_t G, M, E, D, T; /* For bandwidth weights */ const routerstatus_format_type_t rs_format = flavor == FLAV_NS ? NS_V3_CONSENSUS : NS_V3_CONSENSUS_MICRODESC; char *params = NULL; @@ -1210,6 +1367,16 @@ networkstatus_compute_consensus(smartlist_t *votes, consensus_method = MAX_SUPPORTED_CONSENSUS_METHOD; } + if (consensus_method >= MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE) { + /* It's smarter to initialize these weights to 1, so that later on, + * we can't accidentally divide by zero. */ + G = M = E = D = 1; + T = 4; + } else { + /* ...but originally, they were set to zero. */ + G = M = E = D = T = 0; + } + /* Compute medians of time-related things, and figure out how many * routers we might need to talk about. */ { @@ -1249,7 +1416,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_free(sv); /* elements get freed later. */ } SMARTLIST_FOREACH(v->known_flags, const char *, cp, - smartlist_add(flags, tor_strdup(cp))); + smartlist_add_strdup(flags, cp)); } SMARTLIST_FOREACH_END(v); valid_after = median_time(va_times, n_votes); fresh_until = median_time(fu_times, n_votes); @@ -1282,7 +1449,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_free(combined_client_versions); if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_VOTING) - smartlist_add(flags, tor_strdup("NoEdConsensus")); + smartlist_add_strdup(flags, "NoEdConsensus"); smartlist_sort_strings(flags); smartlist_uniq_strings(flags); @@ -1331,13 +1498,24 @@ networkstatus_compute_consensus(smartlist_t *votes, tor_free(flaglist); } + if (consensus_method >= MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS) { + int num_dirauth = get_n_authorities(V3_DIRINFO); + int idx; + for (idx = 0; idx < 4; ++idx) { + char *proto_line = compute_nth_protocol_set(idx, num_dirauth, votes); + if (BUG(!proto_line)) + continue; + smartlist_add(chunks, proto_line); + } + } + param_list = dirvote_compute_params(votes, consensus_method, total_authorities); if (smartlist_len(param_list)) { params = smartlist_join_strings(param_list, " ", 0, NULL); - smartlist_add(chunks, tor_strdup("params ")); + smartlist_add_strdup(chunks, "params "); smartlist_add(chunks, params); - smartlist_add(chunks, tor_strdup("\n")); + smartlist_add_strdup(chunks, "\n"); } if (consensus_method >= MIN_METHOD_FOR_SHARED_RANDOM) { @@ -1438,6 +1616,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_t *matching_descs = smartlist_new(); smartlist_t *chosen_flags = smartlist_new(); smartlist_t *versions = smartlist_new(); + smartlist_t *protocols = smartlist_new(); smartlist_t *exitsummaries = smartlist_new(); uint32_t *bandwidths_kb = tor_calloc(smartlist_len(votes), sizeof(uint32_t)); @@ -1580,9 +1759,10 @@ networkstatus_compute_consensus(smartlist_t *votes, routerstatus_t rs_out; const char *current_rsa_id = NULL; const char *chosen_version; + const char *chosen_protocol_list; const char *chosen_name = NULL; int exitsummary_disagreement = 0; - int is_named = 0, is_unnamed = 0, is_running = 0; + int is_named = 0, is_unnamed = 0, is_running = 0, is_valid = 0; int is_guard = 0, is_exit = 0, is_bad_exit = 0; int naming_conflict = 0; int n_listing = 0; @@ -1593,6 +1773,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_clear(matching_descs); smartlist_clear(chosen_flags); smartlist_clear(versions); + smartlist_clear(protocols); num_bandwidths = 0; num_mbws = 0; num_guardfraction_inputs = 0; @@ -1612,6 +1793,12 @@ networkstatus_compute_consensus(smartlist_t *votes, if (rs->version && rs->version[0]) smartlist_add(versions, rs->version); + if (rs->protocols) { + /* We include this one even if it's empty: voting for an + * empty protocol list actually is meaningful. */ + smartlist_add(protocols, rs->protocols); + } + /* Tally up all the flags. */ for (int flag = 0; flag < n_voter_flags[voter_idx]; ++flag) { if (rs->flags & (U64_LITERAL(1) << flag)) @@ -1733,6 +1920,8 @@ networkstatus_compute_consensus(smartlist_t *votes, is_running = 1; else if (!strcmp(fl, "BadExit")) is_bad_exit = 1; + else if (!strcmp(fl, "Valid")) + is_valid = 1; } } } SMARTLIST_FOREACH_END(fl); @@ -1742,6 +1931,12 @@ networkstatus_compute_consensus(smartlist_t *votes, if (!is_running) continue; + /* Starting with consensus method 24, we don't list servers + * that are not valid in a consensus. See Proposal 272 */ + if (!is_valid && + consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES) + continue; + /* Pick the version. */ if (smartlist_len(versions)) { sort_version_list(versions, 0); @@ -1750,6 +1945,14 @@ networkstatus_compute_consensus(smartlist_t *votes, chosen_version = NULL; } + /* Pick the protocol list */ + if (smartlist_len(protocols)) { + smartlist_sort_strings(protocols); + chosen_protocol_list = get_most_frequent_member(protocols); + } else { + chosen_protocol_list = NULL; + } + /* If it's a guard and we have enough guardfraction votes, calculate its consensus guardfraction value. */ if (is_guard && num_guardfraction_inputs > 2 && @@ -1883,7 +2086,7 @@ networkstatus_compute_consensus(smartlist_t *votes, char *buf; /* Okay!! Now we can write the descriptor... */ /* First line goes into "buf". */ - buf = routerstatus_format_entry(&rs_out, NULL, rs_format, NULL); + buf = routerstatus_format_entry(&rs_out, NULL, NULL, rs_format, NULL); if (buf) smartlist_add(chunks, buf); } @@ -1899,10 +2102,14 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_join_strings(chosen_flags, " ", 0, NULL)); /* Now the version line. */ if (chosen_version) { - smartlist_add(chunks, tor_strdup("\nv ")); - smartlist_add(chunks, tor_strdup(chosen_version)); + smartlist_add_strdup(chunks, "\nv "); + smartlist_add_strdup(chunks, chosen_version); + } + smartlist_add_strdup(chunks, "\n"); + if (chosen_protocol_list && + consensus_method >= MIN_METHOD_FOR_RS_PROTOCOLS) { + smartlist_add_asprintf(chunks, "pr %s\n", chosen_protocol_list); } - smartlist_add(chunks, tor_strdup("\n")); /* Now the weight line. */ if (rs_out.has_bandwidth) { char *guardfraction_str = NULL; @@ -1943,6 +2150,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_free(matching_descs); smartlist_free(chosen_flags); smartlist_free(versions); + smartlist_free(protocols); smartlist_free(exitsummaries); tor_free(bandwidths_kb); tor_free(measured_bws_kb); @@ -1950,7 +2158,7 @@ networkstatus_compute_consensus(smartlist_t *votes, } /* Mark the directory footer region */ - smartlist_add(chunks, tor_strdup("directory-footer\n")); + smartlist_add_strdup(chunks, "directory-footer\n"); { int64_t weight_scale = BW_WEIGHT_SCALE; @@ -2001,7 +2209,7 @@ networkstatus_compute_consensus(smartlist_t *votes, const char *algname = crypto_digest_algorithm_get_name(digest_alg); char *signature; - smartlist_add(chunks, tor_strdup("directory-signature ")); + smartlist_add_strdup(chunks, "directory-signature "); /* Compute the hash of the chunks. */ crypto_digest_smartlist(digest, digest_len, chunks, "", digest_alg); @@ -2028,7 +2236,7 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_add(chunks, signature); if (legacy_id_key_digest && legacy_signing_key) { - smartlist_add(chunks, tor_strdup("directory-signature ")); + smartlist_add_strdup(chunks, "directory-signature "); base16_encode(fingerprint, sizeof(fingerprint), legacy_id_key_digest, DIGEST_LEN); crypto_pk_get_fingerprint(legacy_signing_key, @@ -2341,7 +2549,7 @@ networkstatus_format_signatures(networkstatus_t *consensus, 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_add_strdup(elements, buf); } SMARTLIST_FOREACH_END(sig); } SMARTLIST_FOREACH_END(v); @@ -3451,8 +3659,8 @@ dirvote_add_signatures(const char *detached_signatures_body, "Queuing it for the next consensus.", source); if (!pending_consensus_signature_list) pending_consensus_signature_list = smartlist_new(); - smartlist_add(pending_consensus_signature_list, - tor_strdup(detached_signatures_body)); + smartlist_add_strdup(pending_consensus_signature_list, + detached_signatures_body); *msg = "Signature queued"; return 0; } diff --git a/src/or/dirvote.h b/src/or/dirvote.h index a1f71ce4bb..ac7db69db2 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -55,7 +55,7 @@ #define MIN_SUPPORTED_CONSENSUS_METHOD 13 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 23 +#define MAX_SUPPORTED_CONSENSUS_METHOD 26 /** Lowest consensus method where microdesc consensuses omit any entry * with no microdesc. */ @@ -99,6 +99,22 @@ * value(s). */ #define MIN_METHOD_FOR_SHARED_RANDOM 23 +/** Lowest consensus method where authorities drop all nodes that don't get + * the Valid flag. */ +#define MIN_METHOD_FOR_EXCLUDING_INVALID_NODES 24 + +/** Lowest consensus method where authorities vote on required/recommended + * protocols. */ +#define MIN_METHOD_FOR_RECOMMENDED_PROTOCOLS 25 + +/** Lowest consensus method where authorities add protocols to routerstatus + * entries. */ +#define MIN_METHOD_FOR_RS_PROTOCOLS 25 + +/** Lowest consensus method where authorities initialize bandwidth weights to 1 + * instead of 0. See #14881 */ +#define MIN_METHOD_FOR_INIT_BW_WEIGHTS_ONE 26 + /** 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.) */ @@ -222,6 +238,10 @@ STATIC smartlist_t *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); +STATIC int +networkstatus_compute_bw_weights_v10(smartlist_t *chunks, int64_t G, + int64_t M, int64_t E, int64_t D, + int64_t T, int64_t weight_scale); #endif #endif diff --git a/src/or/dns.c b/src/or/dns.c index aaffad77fc..388104f8da 100644 --- a/src/or/dns.c +++ b/src/or/dns.c @@ -9,6 +9,42 @@ * This is implemented as a wrapper around Adam Langley's eventdns.c code. * (We can't just use gethostbyname() and friends because we really need to * be nonblocking.) + * + * There are three main cases when a Tor relay uses dns.c to launch a DNS + * request: + * <ol> + * <li>To check whether the DNS server is working more or less correctly. + * This happens via dns_launch_correctness_checks(). The answer is + * reported in the return value from later calls to + * dns_seems_to_be_broken(). + * <li>When a client has asked the relay, in a RELAY_BEGIN cell, to connect + * to a given server by hostname. This happens via dns_resolve(). + * <li>When a client has asked the rela, in a RELAY_RESOLVE cell, to look + * up a given server's IP address(es) by hostname. This also happens via + * dns_resolve(). + * </ol> + * + * Each of these gets handled a little differently. + * + * To check for correctness, we look up some hostname we expect to exist and + * have real entries, some hostnames which we expect to definitely not exist, + * and some hostnames that we expect to probably not exist. If too many of + * the hostnames that shouldn't exist do exist, that's a DNS hijacking + * attempt. If too many of the hostnames that should exist have the same + * addresses as the ones that shouldn't exist, that's a very bad DNS hijacking + * attempt, or a very naughty captive portal. And if the hostnames that + * should exist simply don't exist, we probably have a broken nameserver. + * + * To handle client requests, we first check our cache for answers. If there + * isn't something up-to-date, we've got to launch A or AAAA requests as + * appropriate. How we handle responses to those in particular is a bit + * complex; see dns_lookup() and set_exitconn_info_from_resolve(). + * + * When a lookup is finally complete, the inform_pending_connections() + * function will tell all of the streams that have been waiting for the + * resolve, by calling connection_exit_connect() if the client sent a + * RELAY_BEGIN cell, and by calling send_resolved_cell() or + * send_hostname_cell() if the client sent a RELAY_RESOLVE cell. **/ #define DNS_PRIVATE @@ -793,8 +829,14 @@ dns_resolve_impl,(edge_connection_t *exitconn, int is_resolve, } /** Given an exit connection <b>exitconn</b>, and a cached_resolve_t - * <b>resolve</b> whose DNS lookups have all succeeded or failed, update the - * appropriate fields (address_ttl and addr) of <b>exitconn</b>. + * <b>resolve</b> whose DNS lookups have all either succeeded or failed, + * update the appropriate fields (address_ttl and addr) of <b>exitconn</b>. + * + * The logic can be complicated here, since we might have launched both + * an A lookup and an AAAA lookup, and since either of those might have + * succeeded or failed, and since we want to answer a RESOLVE cell with + * a full answer but answer a BEGIN cell with whatever answer the client + * would accept <i>and</i> we could still connect to. * * If this is a reverse lookup, set *<b>hostname_out</b> to a newly allocated * copy of the name resulting hostname. @@ -1137,7 +1179,12 @@ dns_found_answer(const char *address, uint8_t query_type, /** Given a pending cached_resolve_t that we just finished resolving, * inform every connection that was waiting for the outcome of that - * resolution. */ + * resolution. + * + * Do this by sending a RELAY_RESOLVED cell (if the pending stream had sent us + * RELAY_RESOLVE cell), or by launching an exit connection (if the pending + * stream had send us a RELAY_BEGIN cell). + */ static void inform_pending_connections(cached_resolve_t *resolve) { @@ -1703,7 +1750,7 @@ wildcard_increment_answer(const char *id) "invalid addresses. Apparently they are hijacking DNS failures. " "I'll try to correct for this by treating future occurrences of " "\"%s\" as 'not found'.", id, *ip, id); - smartlist_add(dns_wildcard_list, tor_strdup(id)); + smartlist_add_strdup(dns_wildcard_list, id); } if (!dns_wildcard_notice_given) control_event_server_status(LOG_NOTICE, "DNS_HIJACKED"); @@ -1727,7 +1774,7 @@ add_wildcarded_test_address(const char *address) n_test_addrs = get_options()->ServerDNSTestAddresses ? smartlist_len(get_options()->ServerDNSTestAddresses) : 0; - smartlist_add(dns_wildcarded_test_address_list, tor_strdup(address)); + smartlist_add_strdup(dns_wildcarded_test_address_list, address); n = smartlist_len(dns_wildcarded_test_address_list); if (n > n_test_addrs/2) { tor_log(dns_wildcarded_test_address_notice_given ? LOG_INFO : LOG_NOTICE, diff --git a/src/or/dns_structs.h b/src/or/dns_structs.h index bb67459d7b..bc6067213d 100644 --- a/src/or/dns_structs.h +++ b/src/or/dns_structs.h @@ -1,3 +1,15 @@ +/* Copyright (c) 2003-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 dns_structs.h + * + * \brief Structures used in dns.c. Exposed to dns.c, and to the unit tests + * that declare DNS_PRIVATE. + */ + #ifndef TOR_DNS_STRUCTS_H #define TOR_DNS_STRUCTS_H diff --git a/src/or/dnsserv.c b/src/or/dnsserv.c index 6aab1e2c36..c5c0a88b09 100644 --- a/src/or/dnsserv.c +++ b/src/or/dnsserv.c @@ -3,10 +3,22 @@ /** * \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. + * \brief Implements client-side DNS proxy server code. + * + * When a user enables the DNSPort configuration option to have their local + * Tor client handle DNS requests, this module handles it. It functions as a + * "DNS Server" on the client side, which client applications use. + * + * Inbound DNS requests are represented as entry_connection_t here (since + * that's how Tor represents client-side streams), which are kept associated + * with an evdns_server_request structure as exposed by Libevent's + * evdns code. + * + * Upon receiving a DNS request, libevent calls our evdns_server_callback() + * function here, which causes this module to create an entry_connection_t + * request as appropriate. Later, when that request is answered, + * connection_edge.c calls dnsserv_resolved() so we can finish up and tell the + * DNS client. **/ #include "or.h" @@ -136,6 +148,8 @@ evdns_server_callback(struct evdns_server_request *req, void *data_) entry_conn->socks_request->command = SOCKS_COMMAND_RESOLVE_PTR; } + /* This serves our DNS port so enable DNS request by default. */ + entry_conn->entry_cfg.dns_request = 1; if (q->type == EVDNS_TYPE_A || q->type == EVDNS_QTYPE_ALL) { entry_conn->entry_cfg.ipv4_traffic = 1; entry_conn->entry_cfg.ipv6_traffic = 0; @@ -288,6 +302,10 @@ evdns_get_orig_address(const struct evdns_server_request *req, case RESOLVED_TYPE_IPV6: type = EVDNS_TYPE_AAAA; break; + case RESOLVED_TYPE_ERROR: + case RESOLVED_TYPE_ERROR_TRANSIENT: + /* Addr doesn't matter, since we're not sending it back in the reply.*/ + return addr; default: tor_fragile_assert(); return addr; diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 265b6dcda1..b3fa31df7b 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -63,17 +63,42 @@ typedef struct { smartlist_t *socks_args; } bridge_info_t; -/** A list of our chosen entry guards. */ -static smartlist_t *entry_guards = NULL; -/** A value of 1 means that the entry_guards list has changed - * and those changes need to be flushed to disk. */ -static int entry_guards_dirty = 0; +/** All the context for guard selection on a particular client */ + +struct guard_selection_s { + /** + * A value of 1 means that guard_selection_t structures have changed + * and those changes need to be flushed to disk. + * + * XXX we don't know how to flush multiple guard contexts to disk yet; + * fix that as soon as any way to change the default exists, or at least + * make sure this gets set on change. + */ + int dirty; + + /** + * A list of our chosen entry guards, as entry_guard_t structures; this + * preserves the pre-Prop271 behavior. + */ + smartlist_t *chosen_entry_guards; + + /** + * When we try to choose an entry guard, should we parse and add + * config's EntryNodes first? This was formerly a global. + */ + int should_add_entry_nodes; +}; + +static smartlist_t *guard_contexts = NULL; +static guard_selection_t *curr_guard_context = NULL; static void bridge_free(bridge_info_t *bridge); -static const node_t *choose_random_entry_impl(cpath_build_state_t *state, +static const node_t *choose_random_entry_impl(guard_selection_t *gs, + cpath_build_state_t *state, int for_directory, dirinfo_type_t dirtype, int *n_options_out); +static guard_selection_t * guard_selection_new(void); static int num_bridges_usable(void); /* Default number of entry guards in the case where the NumEntryGuards @@ -84,13 +109,52 @@ static int num_bridges_usable(void); #define MIN_N_GUARDS 1 #define MAX_N_GUARDS 10 -/** Return the list of entry guards, creating it if necessary. */ +/** Allocate a new guard_selection_t */ + +static guard_selection_t * +guard_selection_new(void) +{ + guard_selection_t *gs; + + gs = tor_malloc_zero(sizeof(*gs)); + gs->chosen_entry_guards = smartlist_new(); + + return gs; +} + +/** Get current default guard_selection_t, creating it if necessary */ +guard_selection_t * +get_guard_selection_info(void) +{ + if (!guard_contexts) { + guard_contexts = smartlist_new(); + } + + if (!curr_guard_context) { + curr_guard_context = guard_selection_new(); + smartlist_add(guard_contexts, curr_guard_context); + } + + return curr_guard_context; +} + +/** Return the list of entry guards for a guard_selection_t, creating it + * if necessary. */ +const smartlist_t * +get_entry_guards_for_guard_selection(guard_selection_t *gs) +{ + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + + return gs->chosen_entry_guards; +} + +/** Return the list of entry guards for the default guard_selection_t, + * creating it if necessary. */ const smartlist_t * get_entry_guards(void) { - if (! entry_guards) - entry_guards = smartlist_new(); - return entry_guards; + return get_entry_guards_for_guard_selection(get_guard_selection_info()); } /** Check whether the entry guard <b>e</b> is usable, given the directory @@ -286,21 +350,28 @@ entry_is_live(const entry_guard_t *e, entry_is_live_flags_t flags, return node; } -/** Return the number of entry guards that we think are usable. */ +/** Return the number of entry guards that we think are usable, in the + * context of the given guard_selection_t */ int -num_live_entry_guards(int for_directory) +num_live_entry_guards_for_guard_selection(guard_selection_t *gs, + int for_directory) { int n = 0; const char *msg; + + tor_assert(gs != NULL); + /* Set the entry node attributes we are interested in. */ entry_is_live_flags_t entry_flags = ENTRY_NEED_CAPACITY; if (!for_directory) { entry_flags |= ENTRY_NEED_DESCRIPTOR; } - if (! entry_guards) + if (!(gs->chosen_entry_guards)) { return 0; - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { + } + + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, entry) { if (for_directory && !entry->is_dir_cache) continue; if (entry_is_live(entry, entry_flags, &msg)) @@ -309,27 +380,57 @@ num_live_entry_guards(int for_directory) return n; } +/** Return the number of entry guards that we think are usable, for the + * default guard selection */ +int +num_live_entry_guards(int for_directory) +{ + return num_live_entry_guards_for_guard_selection( + get_guard_selection_info(), for_directory); +} + /** If <b>digest</b> matches the identity of any node in the - * entry_guards list, return that node. Else return NULL. */ + * entry_guards list for the provided guard selection state, + return that node. Else return NULL. */ entry_guard_t * -entry_guard_get_by_id_digest(const char *digest) +entry_guard_get_by_id_digest_for_guard_selection(guard_selection_t *gs, + const char *digest) { - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, entry, + tor_assert(gs != NULL); + + SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, entry, if (tor_memeq(digest, entry->identity, DIGEST_LEN)) return entry; ); return NULL; } -/** Dump a description of our list of entry guards to the log at level - * <b>severity</b>. */ +/** If <b>digest</b> matches the identity of any node in the + * entry_guards list for the default guard selection state, + return that node. Else return NULL. */ +entry_guard_t * +entry_guard_get_by_id_digest(const char *digest) +{ + return entry_guard_get_by_id_digest_for_guard_selection( + get_guard_selection_info(), digest); +} + +/** Dump a description of our list of entry guards in the given guard + * selection context to the log at level <b>severity</b>. */ static void -log_entry_guards(int severity) +log_entry_guards_for_guard_selection(guard_selection_t *gs, int severity) { smartlist_t *elements = smartlist_new(); char *s; - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) + /* + * TODO this should probably log more info about prop-271 state too + * when it's implemented. + */ + + tor_assert(gs != NULL); + + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { const char *msg = NULL; if (entry_is_live(e, ENTRY_NEED_CAPACITY, &msg)) @@ -386,23 +487,28 @@ control_event_guard_deferred(void) /** Largest amount that we'll backdate chosen_on_date */ #define CHOSEN_ON_DATE_SLOP (30*86400) -/** Add a new (preferably stable and fast) router to our - * entry_guards list. Return a pointer to the router if we succeed, - * or NULL if we can't find any more suitable entries. +/** Add a new (preferably stable and fast) router to our chosen_entry_guards + * list for the supplied guard selection. Return a pointer to the router if + * we succeed, or NULL if we can't find any more suitable entries. * * If <b>chosen</b> is defined, use that one, and if it's not * already in our entry_guards list, put it at the *beginning*. * Else, put the one we pick at the end of the list. */ STATIC const node_t * -add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, +add_an_entry_guard(guard_selection_t *gs, + const node_t *chosen, int reset_status, int prepend, int for_discovery, int for_directory) { const node_t *node; entry_guard_t *entry; + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + if (chosen) { node = chosen; - entry = entry_guard_get_by_id_digest(node->identity); + entry = entry_guard_get_by_id_digest_for_guard_selection(gs, + node->identity); if (entry) { if (reset_status) { entry->bad_since = 0; @@ -428,13 +534,11 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, if (!node) return NULL; } - if (node->using_as_guard) - return NULL; - if (entry_guard_get_by_id_digest(node->identity) != NULL) { + if (entry_guard_get_by_id_digest_for_guard_selection(gs, node->identity) + != NULL) { log_info(LD_CIRC, "I was about to add a duplicate entry guard."); /* This can happen if we choose a guard, then the node goes away, then * comes back. */ - ((node_t*) node)->using_as_guard = 1; return NULL; } entry = tor_malloc_zero(sizeof(entry_guard_t)); @@ -466,14 +570,15 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend, if (!for_discovery) entry->made_contact = 1; - ((node_t*)node)->using_as_guard = 1; if (prepend) - smartlist_insert(entry_guards, 0, entry); + smartlist_insert(gs->chosen_entry_guards, 0, entry); else - smartlist_add(entry_guards, entry); + smartlist_add(gs->chosen_entry_guards, entry); + control_event_guard(entry->nickname, entry->identity, "NEW"); control_event_guard_deferred(); - log_entry_guards(LOG_INFO); + log_entry_guards_for_guard_selection(gs, LOG_INFO); + return node; } @@ -503,20 +608,25 @@ decide_num_guards(const or_options_t *options, int for_directory) /** If the use of entry guards is configured, choose more entry guards * until we have enough in the list. */ static void -pick_entry_guards(const or_options_t *options, int for_directory) +pick_entry_guards(guard_selection_t *gs, + const or_options_t *options, + int for_directory) { int changed = 0; const int num_needed = decide_num_guards(options, for_directory); - tor_assert(entry_guards); + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); - while (num_live_entry_guards(for_directory) < num_needed) { - if (!add_an_entry_guard(NULL, 0, 0, 0, for_directory)) + while (num_live_entry_guards_for_guard_selection(gs, for_directory) + < num_needed) { + if (!add_an_entry_guard(gs, NULL, 0, 0, 0, for_directory)) break; changed = 1; } + if (changed) - entry_guards_changed(); + entry_guards_changed_for_guard_selection(gs); } /** How long (in seconds) do we allow an entry guard to be nonfunctional, @@ -559,19 +669,23 @@ guards_get_lifetime(void) MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; } -/** Remove any entry guard which was selected by an unknown version of Tor, - * or which was selected by a version of Tor that's known to select - * entry guards badly, or which was selected more 2 months ago. */ +/** Remove from a guard selection context any entry guard which was selected + * by an unknown version of Tor, or which was selected by a version of Tor + * that's known to select entry guards badly, or which was selected more 2 + * months ago. */ /* XXXX The "obsolete guards" and "chosen long ago guards" things should * probably be different functions. */ static int -remove_obsolete_entry_guards(time_t now) +remove_obsolete_entry_guards(guard_selection_t *gs, time_t now) { int changed = 0, i; int32_t guard_lifetime = guards_get_lifetime(); - for (i = 0; i < smartlist_len(entry_guards); ++i) { - entry_guard_t *entry = smartlist_get(entry_guards, i); + tor_assert(gs != NULL); + if (!(gs->chosen_entry_guards)) goto done; + + for (i = 0; i < smartlist_len(gs->chosen_entry_guards); ++i) { + entry_guard_t *entry = smartlist_get(gs->chosen_entry_guards, i); const char *ver = entry->chosen_by_version; const char *msg = NULL; tor_version_t v; @@ -598,28 +712,32 @@ remove_obsolete_entry_guards(time_t now) entry->nickname, dbuf, msg, ver?escaped(ver):"none"); control_event_guard(entry->nickname, entry->identity, "DROPPED"); entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, i--); - log_entry_guards(LOG_INFO); + smartlist_del_keeporder(gs->chosen_entry_guards, i--); + log_entry_guards_for_guard_selection(gs, LOG_INFO); changed = 1; } } + done: return changed ? 1 : 0; } -/** Remove all entry guards that have been down or unlisted for so - * long that we don't think they'll come up again. Return 1 if we - * removed any, or 0 if we did nothing. */ +/** Remove all entry guards from this guard selection context that have + * been down or unlisted for so long that we don't think they'll come up + * again. Return 1 if we removed any, or 0 if we did nothing. */ static int -remove_dead_entry_guards(time_t now) +remove_dead_entry_guards(guard_selection_t *gs, time_t now) { char dbuf[HEX_DIGEST_LEN+1]; char tbuf[ISO_TIME_LEN+1]; int i; int changed = 0; - for (i = 0; i < smartlist_len(entry_guards); ) { - entry_guard_t *entry = smartlist_get(entry_guards, i); + tor_assert(gs != NULL); + if (!(gs->chosen_entry_guards)) goto done; + + for (i = 0; i < smartlist_len(gs->chosen_entry_guards); ) { + entry_guard_t *entry = smartlist_get(gs->chosen_entry_guards, i); if (entry->bad_since && ! entry->path_bias_disabled && entry->bad_since + ENTRY_GUARD_REMOVE_AFTER < now) { @@ -631,32 +749,47 @@ remove_dead_entry_guards(time_t now) entry->nickname, dbuf, tbuf); control_event_guard(entry->nickname, entry->identity, "DROPPED"); entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, i); - log_entry_guards(LOG_INFO); + smartlist_del_keeporder(gs->chosen_entry_guards, i); + log_entry_guards_for_guard_selection(gs, LOG_INFO); changed = 1; } else ++i; } + + done: return changed ? 1 : 0; } -/** Remove all currently listed entry guards. So new ones will be chosen. */ +/** Remove all currently listed entry guards for a given guard selection + * context */ void -remove_all_entry_guards(void) +remove_all_entry_guards_for_guard_selection(guard_selection_t *gs) { char dbuf[HEX_DIGEST_LEN+1]; - while (smartlist_len(entry_guards)) { - entry_guard_t *entry = smartlist_get(entry_guards, 0); - base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); - log_info(LD_CIRC, "Entry guard '%s' (%s) has been dropped.", - entry->nickname, dbuf); - control_event_guard(entry->nickname, entry->identity, "DROPPED"); - entry_guard_free(entry); - smartlist_del(entry_guards, 0); + tor_assert(gs != NULL); + + if (gs->chosen_entry_guards) { + while (smartlist_len(gs->chosen_entry_guards)) { + entry_guard_t *entry = smartlist_get(gs->chosen_entry_guards, 0); + base16_encode(dbuf, sizeof(dbuf), entry->identity, DIGEST_LEN); + log_info(LD_CIRC, "Entry guard '%s' (%s) has been dropped.", + entry->nickname, dbuf); + control_event_guard(entry->nickname, entry->identity, "DROPPED"); + entry_guard_free(entry); + smartlist_del(gs->chosen_entry_guards, 0); + } } - log_entry_guards(LOG_INFO); - entry_guards_changed(); + + log_entry_guards_for_guard_selection(gs, LOG_INFO); + entry_guards_changed_for_guard_selection(gs); +} + +/** Remove all currently listed entry guards. So new ones will be chosen. */ +void +remove_all_entry_guards(void) +{ + remove_all_entry_guards_for_guard_selection(get_guard_selection_info()); } /** A new directory or router-status has arrived; update the down/listed @@ -669,19 +802,21 @@ remove_all_entry_guards(void) * think that things are unlisted. */ void -entry_guards_compute_status(const or_options_t *options, time_t now) +entry_guards_compute_status_for_guard_selection(guard_selection_t *gs, + const or_options_t *options, + time_t now) { int changed = 0; digestmap_t *reasons; - if (! entry_guards) + if ((!gs) || !(gs->chosen_entry_guards)) return; if (options->EntryNodes) /* reshuffle the entry guard list if needed */ entry_nodes_should_be_added(); reasons = digestmap_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, entry) { const node_t *r = node_get_by_id(entry->identity); const char *reason = NULL; @@ -695,13 +830,14 @@ entry_guards_compute_status(const or_options_t *options, time_t now) } SMARTLIST_FOREACH_END(entry); - if (remove_dead_entry_guards(now)) + if (remove_dead_entry_guards(gs, now)) changed = 1; - if (remove_obsolete_entry_guards(now)) + if (remove_obsolete_entry_guards(gs, now)) changed = 1; if (changed) { - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, + entry) { const char *reason = digestmap_get(reasons, entry->identity); const char *live_msg = ""; const node_t *r = entry_is_live(entry, ENTRY_NEED_CAPACITY, &live_msg); @@ -716,14 +852,31 @@ entry_guards_compute_status(const or_options_t *options, time_t now) r ? "" : live_msg); } SMARTLIST_FOREACH_END(entry); log_info(LD_CIRC, " (%d/%d entry guards are usable/new)", - num_live_entry_guards(0), smartlist_len(entry_guards)); - log_entry_guards(LOG_INFO); - entry_guards_changed(); + num_live_entry_guards_for_guard_selection(gs, 0), + smartlist_len(gs->chosen_entry_guards)); + log_entry_guards_for_guard_selection(gs, LOG_INFO); + entry_guards_changed_for_guard_selection(gs); } digestmap_free(reasons, NULL); } +/** A new directory or router-status has arrived; update the down/listed + * status of the entry guards. + * + * An entry is 'down' if the directory lists it as nonrunning. + * An entry is 'unlisted' if the directory doesn't include it. + * + * Don't call this on startup; only on a fresh download. Otherwise we'll + * think that things are unlisted. + */ +void +entry_guards_compute_status(const or_options_t *options, time_t now) +{ + entry_guards_compute_status_for_guard_selection(get_guard_selection_info(), + options, now); +} + /** Called when a connection to an OR with the identity digest <b>digest</b> * is established (<b>succeeded</b>==1) or has failed (<b>succeeded</b>==0). * If the OR is an entry, change that entry's up/down status. @@ -736,8 +889,9 @@ entry_guards_compute_status(const or_options_t *options, time_t now) * Too many boolean arguments is a recipe for confusion. */ int -entry_guard_register_connect_status(const char *digest, int succeeded, - int mark_relay_status, time_t now) +entry_guard_register_connect_status_for_guard_selection( + guard_selection_t *gs, const char *digest, int succeeded, + int mark_relay_status, time_t now) { int changed = 0; int refuse_conn = 0; @@ -746,10 +900,11 @@ entry_guard_register_connect_status(const char *digest, int succeeded, int idx = -1; char buf[HEX_DIGEST_LEN+1]; - if (! entry_guards) + if (!(gs) || !(gs->chosen_entry_guards)) { return 0; + } - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { tor_assert(e); if (tor_memeq(e->identity, digest, DIGEST_LEN)) { entry = e; @@ -784,11 +939,12 @@ entry_guard_register_connect_status(const char *digest, int succeeded, "Connection to never-contacted entry guard '%s' (%s) failed. " "Removing from the list. %d/%d entry guards usable/new.", entry->nickname, buf, - num_live_entry_guards(0)-1, smartlist_len(entry_guards)-1); + num_live_entry_guards_for_guard_selection(gs, 0) - 1, + smartlist_len(gs->chosen_entry_guards)-1); control_event_guard(entry->nickname, entry->identity, "DROPPED"); entry_guard_free(entry); - smartlist_del_keeporder(entry_guards, idx); - log_entry_guards(LOG_INFO); + smartlist_del_keeporder(gs->chosen_entry_guards, idx); + log_entry_guards_for_guard_selection(gs, LOG_INFO); changed = 1; } else if (!entry->unreachable_since) { log_info(LD_CIRC, "Unable to connect to entry guard '%s' (%s). " @@ -818,7 +974,7 @@ entry_guard_register_connect_status(const char *digest, int succeeded, * came back? We should give our earlier entries another try too, * and close this connection so we don't use it before we've given * the others a shot. */ - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { if (e == entry) break; if (e->made_contact) { @@ -837,56 +993,68 @@ entry_guard_register_connect_status(const char *digest, int succeeded, "Connected to new entry guard '%s' (%s). Marking earlier " "entry guards up. %d/%d entry guards usable/new.", entry->nickname, buf, - num_live_entry_guards(0), smartlist_len(entry_guards)); - log_entry_guards(LOG_INFO); + num_live_entry_guards_for_guard_selection(gs, 0), + smartlist_len(gs->chosen_entry_guards)); + log_entry_guards_for_guard_selection(gs, LOG_INFO); changed = 1; } } if (changed) - entry_guards_changed(); + entry_guards_changed_for_guard_selection(gs); return refuse_conn ? -1 : 0; } -/** When we try to choose an entry guard, should we parse and add - * config's EntryNodes first? */ -static int should_add_entry_nodes = 0; +/** Called when a connection to an OR with the identity digest <b>digest</b> + * is established (<b>succeeded</b>==1) or has failed (<b>succeeded</b>==0). + * If the OR is an entry, change that entry's up/down status in the default + * guard selection context. + * Return 0 normally, or -1 if we want to tear down the new connection. + * + * If <b>mark_relay_status</b>, also call router_set_status() on this + * relay. + */ +int +entry_guard_register_connect_status(const char *digest, int succeeded, + int mark_relay_status, time_t now) +{ + return entry_guard_register_connect_status_for_guard_selection( + get_guard_selection_info(), digest, succeeded, mark_relay_status, now); +} /** Called when the value of EntryNodes changes in our configuration. */ void -entry_nodes_should_be_added(void) +entry_nodes_should_be_added_for_guard_selection(guard_selection_t *gs) { + tor_assert(gs != NULL); + log_info(LD_CIRC, "EntryNodes config option set. Putting configured " "relays at the front of the entry guard list."); - should_add_entry_nodes = 1; + gs->should_add_entry_nodes = 1; } -/** Update the using_as_guard fields of all the nodes. We do this after we - * remove entry guards from the list: This is the only function that clears - * the using_as_guard field. */ -static void -update_node_guard_status(void) -{ - smartlist_t *nodes = nodelist_get_list(); - SMARTLIST_FOREACH(nodes, node_t *, node, node->using_as_guard = 0); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, entry) { - node_t *node = node_get_mutable_by_id(entry->identity); - if (node) - node->using_as_guard = 1; - } SMARTLIST_FOREACH_END(entry); +/** Called when the value of EntryNodes changes in our configuration. */ +void +entry_nodes_should_be_added(void) +{ + entry_nodes_should_be_added_for_guard_selection( + get_guard_selection_info()); } /** Adjust the entry guards list so that it only contains entries from * EntryNodes, adding new entries from EntryNodes to the list as needed. */ STATIC void -entry_guards_set_from_config(const or_options_t *options) +entry_guards_set_from_config(guard_selection_t *gs, + const or_options_t *options) { smartlist_t *entry_nodes, *worse_entry_nodes, *entry_fps; smartlist_t *old_entry_guards_on_list, *old_entry_guards_not_on_list; const int numentryguards = decide_num_guards(options, 0); - tor_assert(entry_guards); - should_add_entry_nodes = 0; + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + + gs->should_add_entry_nodes = 0; if (!options->EntryNodes) { /* It's possible that a controller set EntryNodes, thus making @@ -915,7 +1083,7 @@ entry_guards_set_from_config(const or_options_t *options) SMARTLIST_FOREACH(entry_nodes, const node_t *,node, smartlist_add(entry_fps, (void*)node->identity)); - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, { + SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, e, { if (smartlist_contains_digest(entry_fps, e->identity)) smartlist_add(old_entry_guards_on_list, e); else @@ -925,7 +1093,8 @@ entry_guards_set_from_config(const or_options_t *options) /* Remove all currently configured guard nodes, excluded nodes, unreachable * nodes, or non-Guard nodes from entry_nodes. */ SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { - if (entry_guard_get_by_id_digest(node->identity)) { + if (entry_guard_get_by_id_digest_for_guard_selection(gs, + node->identity)) { SMARTLIST_DEL_CURRENT(entry_nodes, node); continue; } else if (routerset_contains_node(options->ExcludeNodes, node)) { @@ -942,9 +1111,9 @@ entry_guards_set_from_config(const or_options_t *options) } SMARTLIST_FOREACH_END(node); /* Now build the new entry_guards list. */ - smartlist_clear(entry_guards); + smartlist_clear(gs->chosen_entry_guards); /* First, the previously configured guards that are in EntryNodes. */ - smartlist_add_all(entry_guards, old_entry_guards_on_list); + smartlist_add_all(gs->chosen_entry_guards, old_entry_guards_on_list); /* Next, scramble the rest of EntryNodes, putting the guards first. */ smartlist_shuffle(entry_nodes); smartlist_shuffle(worse_entry_nodes); @@ -952,24 +1121,23 @@ entry_guards_set_from_config(const or_options_t *options) /* Next, the rest of EntryNodes */ SMARTLIST_FOREACH_BEGIN(entry_nodes, const node_t *, node) { - add_an_entry_guard(node, 0, 0, 1, 0); - if (smartlist_len(entry_guards) > numentryguards * 10) + add_an_entry_guard(gs, node, 0, 0, 1, 0); + if (smartlist_len(gs->chosen_entry_guards) > numentryguards * 10) break; } SMARTLIST_FOREACH_END(node); - log_notice(LD_GENERAL, "%d entries in guards", smartlist_len(entry_guards)); + log_notice(LD_GENERAL, "%d entries in guards", + smartlist_len(gs->chosen_entry_guards)); /* Finally, free the remaining previously configured guards that are not in * EntryNodes. */ SMARTLIST_FOREACH(old_entry_guards_not_on_list, entry_guard_t *, e, entry_guard_free(e)); - update_node_guard_status(); - smartlist_free(entry_nodes); smartlist_free(worse_entry_nodes); smartlist_free(entry_fps); smartlist_free(old_entry_guards_on_list); smartlist_free(old_entry_guards_not_on_list); - entry_guards_changed(); + entry_guards_changed_for_guard_selection(gs); } /** Return 0 if we're fine adding arbitrary routers out of the @@ -996,7 +1164,8 @@ entry_list_is_constrained(const or_options_t *options) const node_t * choose_random_entry(cpath_build_state_t *state) { - return choose_random_entry_impl(state, 0, NO_DIRINFO, NULL); + return choose_random_entry_impl(get_guard_selection_info(), + state, 0, NO_DIRINFO, NULL); } /** Pick a live (up and listed) directory guard from entry_guards for @@ -1004,7 +1173,8 @@ choose_random_entry(cpath_build_state_t *state) const node_t * choose_random_dirguard(dirinfo_type_t type) { - return choose_random_entry_impl(NULL, 1, type, NULL); + return choose_random_entry_impl(get_guard_selection_info(), + NULL, 1, type, NULL); } /** Filter <b>all_entry_guards</b> for usable entry guards and put them @@ -1095,7 +1265,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, return retval; } -/** Pick a node to be used as the entry guard of a circuit. +/** Pick a node to be used as the entry guard of a circuit, relative to + * a supplied guard selection context. * * If <b>state</b> is set, it contains the information we know about * the upcoming circuit. @@ -1116,7 +1287,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards, * Helper for choose_random{entry,dirguard}. */ static const node_t * -choose_random_entry_impl(cpath_build_state_t *state, int for_directory, +choose_random_entry_impl(guard_selection_t *gs, + cpath_build_state_t *state, int for_directory, dirinfo_type_t dirinfo_type, int *n_options_out) { const or_options_t *options = get_options(); @@ -1130,18 +1302,18 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory, const int num_needed = decide_num_guards(options, for_directory); int retval = 0; + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + if (n_options_out) *n_options_out = 0; - if (!entry_guards) - entry_guards = smartlist_new(); - - if (should_add_entry_nodes) - entry_guards_set_from_config(options); + if (gs->should_add_entry_nodes) + entry_guards_set_from_config(gs, options); if (!entry_list_is_constrained(options) && - smartlist_len(entry_guards) < num_needed) - pick_entry_guards(options, for_directory); + smartlist_len(gs->chosen_entry_guards) < num_needed) + pick_entry_guards(gs, options, for_directory); retry: smartlist_clear(live_entry_guards); @@ -1149,7 +1321,7 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory, /* Populate the list of live entry guards so that we pick one of them. */ retval = populate_live_entry_guards(live_entry_guards, - entry_guards, + gs->chosen_entry_guards, chosen_exit, dirinfo_type, for_directory, @@ -1177,9 +1349,9 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory, /* XXX if guard doesn't imply fast and stable, then we need * to tell add_an_entry_guard below what we want, or it might * be a long time til we get it. -RD */ - node = add_an_entry_guard(NULL, 0, 0, 1, for_directory); + node = add_an_entry_guard(gs, NULL, 0, 0, 1, for_directory); if (node) { - entry_guards_changed(); + entry_guards_changed_for_guard_selection(gs); /* XXX we start over here in case the new node we added shares * a family with our exit node. There's a chance that we'll just * load up on entry guards here, if the network we're using is @@ -1219,13 +1391,15 @@ choose_random_entry_impl(cpath_build_state_t *state, int for_directory, } /** Parse <b>state</b> and learn about the entry guards it describes. - * If <b>set</b> is true, and there are no errors, replace the global - * entry_list with what we find. + * If <b>set</b> is true, and there are no errors, replace the guard + * list in the provided guard selection context with what we find. * On success, return 0. On failure, alloc into *<b>msg</b> a string * describing the error, and return -1. */ int -entry_guards_parse_state(or_state_t *state, int set, char **msg) +entry_guards_parse_state_for_guard_selection( + guard_selection_t *gs, + or_state_t *state, int set, char **msg) { entry_guard_t *node = NULL; smartlist_t *new_entry_guards = smartlist_new(); @@ -1234,6 +1408,8 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) const char *state_version = state->TorVersion; digestmap_t *added_by = digestmap_new(); + tor_assert(gs != NULL); + *msg = NULL; for (line = state->EntryGuards; line; line = line->next) { if (!strcasecmp(line->key, "EntryGuard")) { @@ -1469,24 +1645,36 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) entry_guard_free(e)); smartlist_free(new_entry_guards); } else { /* !err && set */ - if (entry_guards) { - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, + if (gs->chosen_entry_guards) { + SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, e, entry_guard_free(e)); - smartlist_free(entry_guards); + smartlist_free(gs->chosen_entry_guards); } - entry_guards = new_entry_guards; - entry_guards_dirty = 0; + gs->chosen_entry_guards = new_entry_guards; + gs->dirty = 0; /* XXX hand new_entry_guards to this func, and move it up a * few lines, so we don't have to re-dirty it */ - if (remove_obsolete_entry_guards(now)) - entry_guards_dirty = 1; - - update_node_guard_status(); + if (remove_obsolete_entry_guards(gs, now)) + gs->dirty = 1; } digestmap_free(added_by, tor_free_); return *msg ? -1 : 0; } +/** Parse <b>state</b> and learn about the entry guards it describes. + * If <b>set</b> is true, and there are no errors, replace the guard + * list in the default guard selection context with what we find. + * On success, return 0. On failure, alloc into *<b>msg</b> a string + * describing the error, and return -1. + */ +int +entry_guards_parse_state(or_state_t *state, int set, char **msg) +{ + return entry_guards_parse_state_for_guard_selection( + get_guard_selection_info(), + state, set, msg); +} + /** How long will we let a change in our guard nodes stay un-saved * when we are trying to avoid disk writes? */ #define SLOW_GUARD_STATE_FLUSH_TIME 600 @@ -1494,15 +1682,18 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg) * when we are not trying to avoid disk writes? */ #define FAST_GUARD_STATE_FLUSH_TIME 30 -/** Our list of entry guards has changed, or some element of one - * of our entry guards has changed. Write the changes to disk within - * the next few minutes. +/** Our list of entry guards has changed for a particular guard selection + * context, or some element of one of our entry guards has changed for one. + * Write the changes to disk within the next few minutes. */ void -entry_guards_changed(void) +entry_guards_changed_for_guard_selection(guard_selection_t *gs) { time_t when; - entry_guards_dirty = 1; + + tor_assert(gs != NULL); + + gs->dirty = 1; if (get_options()->AvoidDiskWrites) when = time(NULL) + SLOW_GUARD_STATE_FLUSH_TIME; @@ -1513,24 +1704,42 @@ entry_guards_changed(void) or_state_mark_dirty(get_or_state(), when); } +/** Our list of entry guards has changed for the default guard selection + * context, or some element of one of our entry guards has changed. Write + * the changes to disk within the next few minutes. + */ +void +entry_guards_changed(void) +{ + entry_guards_changed_for_guard_selection(get_guard_selection_info()); +} + /** If the entry guard info has not changed, do nothing and return. * Otherwise, free the EntryGuards piece of <b>state</b> and create * a new one out of the global entry_guards list, and then mark * <b>state</b> dirty so it will get saved to disk. + * + * XXX this should get totally redesigned around storing multiple + * entry guard contexts. For the initial refactor we'll just + * always use the current default. Fix it as soon as we actually + * have any way that default can change. */ void entry_guards_update_state(or_state_t *state) { config_line_t **next, *line; - if (! entry_guards_dirty) + guard_selection_t *gs = get_guard_selection_info(); + + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + + if (!gs->dirty) return; config_free_lines(state->EntryGuards); next = &state->EntryGuards; *next = NULL; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { char dbuf[HEX_DIGEST_LEN+1]; if (!e->made_contact) continue; /* don't write this one to disk */ @@ -1596,7 +1805,7 @@ entry_guards_update_state(or_state_t *state) } SMARTLIST_FOREACH_END(e); if (!get_options()->AvoidDiskWrites) or_state_mark_dirty(get_or_state(), 0); - entry_guards_dirty = 0; + gs->dirty = 0; } /** If <b>question</b> is the string "entry-guards", then dump @@ -1604,12 +1813,20 @@ entry_guards_update_state(or_state_t *state) * the nodes in the global entry_guards list. See control-spec.txt * for details. * For backward compatibility, we also handle the string "helper-nodes". + * + * XXX this should be totally redesigned after prop 271 too, and that's + * going to take some control spec work. * */ int getinfo_helper_entry_guards(control_connection_t *conn, const char *question, char **answer, const char **errmsg) { + guard_selection_t *gs = get_guard_selection_info(); + + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + (void) conn; (void) errmsg; @@ -1618,9 +1835,8 @@ getinfo_helper_entry_guards(control_connection_t *conn, smartlist_t *sl = smartlist_new(); char tbuf[ISO_TIME_LEN+1]; char nbuf[MAX_VERBOSE_NICKNAME_LEN+1]; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { const char *status = NULL; time_t when = 0; const node_t *node; @@ -2042,6 +2258,42 @@ bridge_add_from_config(bridge_line_t *bridge_line) smartlist_add(bridge_list, b); } +/** Returns true iff the node is used as a guard in the specified guard + * context */ +int +is_node_used_as_guard_for_guard_selection(guard_selection_t *gs, + const node_t *node) +{ + int res = 0; + + /* + * We used to have a using_as_guard flag in node_t, but it had to go away + * to allow for multiple guard selection contexts. Instead, search the + * guard list for a matching digest. + */ + + tor_assert(gs != NULL); + tor_assert(node != NULL); + + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { + if (tor_memeq(e->identity, node->identity, DIGEST_LEN)) { + res = 1; + break; + } + } SMARTLIST_FOREACH_END(e); + + return res; +} + +/** Returns true iff the node is used as a guard in the default guard + * context */ +MOCK_IMPL(int, +is_node_used_as_guard, (const node_t *node)) +{ + return is_node_used_as_guard_for_guard_selection( + get_guard_selection_info(), node); +} + /** Return true iff <b>routerset</b> contains the bridge <b>bridge</b>. */ static int routerset_contains_bridge(const routerset_t *routerset, @@ -2383,7 +2635,7 @@ learned_bridge_descriptor(routerinfo_t *ri, int from_cache) fmt_and_decorate_addr(&bridge->addr), (int) bridge->port); } - add_an_entry_guard(node, 1, 1, 0, 0); + add_an_entry_guard(get_guard_selection_info(), node, 1, 1, 0, 0); log_notice(LD_DIR, "new bridge descriptor '%s' (%s): %s", ri->nickname, from_cache ? "cached" : "fresh", router_describe(ri)); @@ -2419,7 +2671,8 @@ num_bridges_usable(void) { int n_options = 0; tor_assert(get_options()->UseBridges); - (void) choose_random_entry_impl(NULL, 0, 0, &n_options); + (void) choose_random_entry_impl(get_guard_selection_info(), + NULL, 0, 0, &n_options); return n_options; } @@ -2472,9 +2725,12 @@ entries_retry_helper(const or_options_t *options, int act) int any_known = 0; int any_running = 0; int need_bridges = options->UseBridges != 0; - if (!entry_guards) - entry_guards = smartlist_new(); - SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) { + guard_selection_t *gs = get_guard_selection_info(); + + tor_assert(gs != NULL); + tor_assert(gs->chosen_entry_guards != NULL); + + SMARTLIST_FOREACH_BEGIN(gs->chosen_entry_guards, entry_guard_t *, e) { node = node_get_by_id(e->identity); if (node && node_has_descriptor(node) && node_is_bridge(node) == need_bridges && @@ -2521,25 +2777,20 @@ entries_retry_all(const or_options_t *options) entries_retry_helper(options, 1); } -/** Return true if at least one of our bridges runs a Tor version that can - * provide microdescriptors to us. If not, we'll fall back to asking for - * full descriptors. */ -int -any_bridge_supports_microdescriptors(void) +/** Free one guard selection context */ +static void +guard_selection_free(guard_selection_t *gs) { - const node_t *node; - if (!get_options()->UseBridges || !entry_guards) - return 0; - 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)) { - /* This is one of our current bridges, and we know enough about - * it to know that it will be able to answer our questions. */ - return 1; - } - } SMARTLIST_FOREACH_END(e); - return 0; + if (!gs) return; + + if (gs->chosen_entry_guards) { + SMARTLIST_FOREACH(gs->chosen_entry_guards, entry_guard_t *, e, + entry_guard_free(e)); + smartlist_free(gs->chosen_entry_guards); + gs->chosen_entry_guards = NULL; + } + + tor_free(gs); } /** Release all storage held by the list of entry guards and related @@ -2547,11 +2798,15 @@ any_bridge_supports_microdescriptors(void) void entry_guards_free_all(void) { - if (entry_guards) { - SMARTLIST_FOREACH(entry_guards, entry_guard_t *, e, - entry_guard_free(e)); - smartlist_free(entry_guards); - entry_guards = NULL; + /* Null out the default */ + curr_guard_context = NULL; + /* Free all the guard contexts */ + if (guard_contexts != NULL) { + SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + guard_selection_free(gs); + } SMARTLIST_FOREACH_END(gs); + smartlist_free(guard_contexts); + guard_contexts = NULL; } clear_bridge_list(); smartlist_free(bridge_list); diff --git a/src/or/entrynodes.h b/src/or/entrynodes.h index 1021e67d43..00f96916b6 100644 --- a/src/or/entrynodes.h +++ b/src/or/entrynodes.h @@ -16,6 +16,9 @@ /* XXXX NM I would prefer that all of this stuff be private to * entrynodes.c. */ +/* Forward declare for guard_selection_t; entrynodes.c has the real struct */ +typedef struct guard_selection_s guard_selection_t; + /** An entry_guard_t represents our information about a chosen long-term * first hop, known as a "helper" node in the literature. We can't just * use a node_t, since we want to remember these even when we @@ -53,6 +56,14 @@ typedef struct entry_guard_t { time_t last_attempted; /**< 0 if we can connect to this guard, or the time * at which we last failed to connect to it. */ + /** + * @name circpathbias fields + * + * These fields are used in circpathbias.c to try to detect entry + * nodes that are failing circuits at a suspicious frequency. + */ + /**@{*/ + double circ_attempts; /**< Number of circuits this guard has "attempted" */ double circ_successes; /**< Number of successfully built circuits using * this guard as first hop. */ @@ -68,20 +79,30 @@ typedef struct entry_guard_t { double use_attempts; /**< Number of circuits we tried to use with streams */ double use_successes; /**< Number of successfully used circuits using * this guard as first hop. */ + /**@}*/ } entry_guard_t; +entry_guard_t *entry_guard_get_by_id_digest_for_guard_selection( + guard_selection_t *gs, const char *digest); entry_guard_t *entry_guard_get_by_id_digest(const char *digest); +void entry_guards_changed_for_guard_selection(guard_selection_t *gs); void entry_guards_changed(void); +guard_selection_t * get_guard_selection_info(void); +const smartlist_t *get_entry_guards_for_guard_selection( + guard_selection_t *gs); const smartlist_t *get_entry_guards(void); +int num_live_entry_guards_for_guard_selection( + guard_selection_t *gs, + int for_directory); int num_live_entry_guards(int for_directory); #endif #ifdef ENTRYNODES_PRIVATE -STATIC const node_t *add_an_entry_guard(const node_t *chosen, +STATIC const node_t *add_an_entry_guard(guard_selection_t *gs, + const node_t *chosen, int reset_status, int prepend, int for_discovery, int for_directory); - STATIC int populate_live_entry_guards(smartlist_t *live_entry_guards, const smartlist_t *all_entry_guards, const node_t *chosen_exit, @@ -90,7 +111,8 @@ STATIC int populate_live_entry_guards(smartlist_t *live_entry_guards, int need_uptime, int need_capacity); STATIC int decide_num_guards(const or_options_t *options, int for_directory); -STATIC void entry_guards_set_from_config(const or_options_t *options); +STATIC void entry_guards_set_from_config(guard_selection_t *gs, + const or_options_t *options); /** Flags to be passed to entry_is_live() to indicate what kind of * entry nodes we are looking for. */ @@ -109,20 +131,32 @@ STATIC int entry_is_time_to_retry(const entry_guard_t *e, time_t now); #endif +void remove_all_entry_guards_for_guard_selection(guard_selection_t *gs); void remove_all_entry_guards(void); +void entry_guards_compute_status_for_guard_selection( + guard_selection_t *gs, const or_options_t *options, time_t now); void entry_guards_compute_status(const or_options_t *options, time_t now); +int entry_guard_register_connect_status_for_guard_selection( + guard_selection_t *gs, const char *digest, int succeeded, + int mark_relay_status, time_t now); int entry_guard_register_connect_status(const char *digest, int succeeded, int mark_relay_status, time_t now); +void entry_nodes_should_be_added_for_guard_selection(guard_selection_t *gs); void entry_nodes_should_be_added(void); int entry_list_is_constrained(const or_options_t *options); const node_t *choose_random_entry(cpath_build_state_t *state); const node_t *choose_random_dirguard(dirinfo_type_t t); +int entry_guards_parse_state_for_guard_selection( + guard_selection_t *gs, or_state_t *state, int set, char **msg); int entry_guards_parse_state(or_state_t *state, int set, char **msg); void entry_guards_update_state(or_state_t *state); int getinfo_helper_entry_guards(control_connection_t *conn, const char *question, char **answer, const char **errmsg); +int is_node_used_as_guard_for_guard_selection(guard_selection_t *gs, + const node_t *node); +MOCK_DECL(int, is_node_used_as_guard, (const node_t *node)); void mark_bridge_list(void); void sweep_bridge_list(void); @@ -143,7 +177,6 @@ int any_bridge_descriptors_known(void); int entries_known_but_down(const or_options_t *options); void entries_retry_all(const or_options_t *options); -int any_bridge_supports_microdescriptors(void); const smartlist_t *get_socks_args_by_bridge_addrport(const tor_addr_t *addr, uint16_t port); diff --git a/src/or/ext_orport.c b/src/or/ext_orport.c index fb7add17d8..676adfd8bf 100644 --- a/src/or/ext_orport.c +++ b/src/or/ext_orport.c @@ -4,7 +4,17 @@ /** * \file ext_orport.c * \brief Code implementing the Extended ORPort. -*/ + * + * The Extended ORPort interface is used by pluggable transports to + * communicate additional information to a Tor bridge, including + * address information. For more information on this interface, + * see pt-spec.txt in torspec.git. + * + * There is no separate structure for extended ORPort connections; they use + * or_connection_t objects, and share most of their implementation with + * connection_or.c. Once the handshake is done, an extended ORPort connection + * turns into a regular OR connection, using connection_ext_or_transition(). + */ #define EXT_ORPORT_PRIVATE #include "or.h" diff --git a/src/or/fallback_dirs.inc b/src/or/fallback_dirs.inc index 245110f4aa..f6289199b7 100644 --- a/src/or/fallback_dirs.inc +++ b/src/or/fallback_dirs.inc @@ -30,9 +30,11 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay " weight=10", "178.62.197.82:80 orport=443 id=0D3EBA17E1C78F1E9900BABDB23861D46FCAF163" " weight=10", -"144.76.14.145:110 orport=143 id=14419131033443AE6E21DA82B0D307F7CAE42BDB" -" ipv6=[2a01:4f8:190:9490::dead]:443" -" 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" @@ -47,8 +49,10 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay * "51.254.215.121:80 orport=443 id=262B66AD25C79588AD1FC8ED0E966395B47E5C1D" * " weight=10", */ -"194.150.168.79:11112 orport=11111 id=29F1020B94BE25E6BE1AD13E93CE19D2131B487C" -" 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" @@ -84,8 +88,10 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay "212.51.134.123:9030 orport=9001 id=50586E25BE067FD1F739998550EDDCB1A14CA5B2" " ipv6=[2a02:168:6e00:0:3a60:77ff:fe9c:8bd1]:9001" " weight=10", -"5.175.233.86:80 orport=443 id=5525D0429BFE5DC4F1B0E9DE47A4CFA169661E33" -" 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" @@ -107,10 +113,14 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay " weight=10", "89.187.142.208:80 orport=443 id=64186650FFE4469EBBE52B644AE543864D32F43C" " weight=10", -"144.76.73.140:9030 orport=9001 id=6A640018EABF3DA9BAD9321AA37C2C87BBE1F907" -" weight=10", -"94.126.23.174:9030 orport=9001 id=6FC6F08270D565BE89B7C819DD8E2D487397C073" -" 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" @@ -240,13 +250,17 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay " weight=10", "5.34.183.205:80 orport=443 id=DDD7871C1B7FA32CB55061E08869A236E61BDDF8" " weight=10", -"195.191.233.221:80 orport=443 id=DE134FC8E5CC4EC8A5DE66934E70AC9D70267197" -" 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", -"176.31.180.157:143 orport=22 id=E781F4EC69671B3F1864AE2753E0890351506329" -" ipv6=[2001:41d0:8:eb9d::1]:22" -" 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" @@ -256,8 +270,10 @@ URL: https:onionoo.torproject.orguptime?first_seen_days=7-&flag=V2Dir&type=relay " weight=10", "46.101.143.173:80 orport=443 id=F960DF50F0FD4075AC9B505C1D4FFC8384C490FB" " weight=10", -"195.154.8.111:80 orport=443 id=FCB6695F8F2DC240E974510A4B3A0F2B12AB5B64" -" 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" diff --git a/src/or/fp_pair.c b/src/or/fp_pair.c index 53b311e580..eeeb0f1de3 100644 --- a/src/or/fp_pair.c +++ b/src/or/fp_pair.c @@ -7,6 +7,14 @@ * \brief Manages data structures for associating pairs of fingerprints. Used * to handle combinations of identity/signing-key fingerprints for * authorities. + * + * This is a nice, simple, compact data structure module that handles a map + * from (signing key fingerprint, identity key fingerprint) to void *. The + * fingerprints here are SHA1 digests of RSA keys. + * + * This structure is used in directory.c and in routerlist.c for handling + * handling authority certificates, since we never want more than a single + * certificate for any (ID key, signing key) pair. **/ #include "or.h" diff --git a/src/or/geoip.c b/src/or/geoip.c index 6eb0ce7669..74811ea643 100644 --- a/src/or/geoip.c +++ b/src/or/geoip.c @@ -7,6 +7,24 @@ * to summarizing client connections by country to entry guards, bridges, * and directory servers; and for statistics on answering network status * requests. + * + * There are two main kinds of functions in this module: geoip functions, + * which map groups of IPv4 and IPv6 addresses to country codes, and + * statistical functions, which collect statistics about different kinds of + * per-country usage. + * + * The geoip lookup tables are implemented as sorted lists of disjoint address + * ranges, each mapping to a singleton geoip_country_t. These country objects + * are also indexed by their names in a hashtable. + * + * The tables are populated from disk at startup by the geoip_load_file() + * function. For more information on the file format they read, see that + * function. See the scripts and the README file in src/config for more + * information about how those files are generated. + * + * Tor uses GeoIP information in order to implement user requests (such as + * ExcludeNodes {cc}), and to keep track of how much usage relays are getting + * for each country. */ #define GEOIP_PRIVATE @@ -862,7 +880,7 @@ geoip_get_transport_history(void) /* If it's the first time we see this transport, note it. */ if (val == 1) - smartlist_add(transports_used, tor_strdup(transport_name)); + smartlist_add_strdup(transports_used, transport_name); log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. " "I've now seen %d clients.", diff --git a/src/or/hibernate.c b/src/or/hibernate.c index 209aae01cf..aaf5c4bdcd 100644 --- a/src/or/hibernate.c +++ b/src/or/hibernate.c @@ -8,6 +8,12 @@ * etc in preparation for closing down or going dormant; and to track * bandwidth and time intervals to know when to hibernate and when to * stop hibernating. + * + * Ordinarily a Tor relay is "Live". + * + * A live relay can stop accepting connections for one of two reasons: either + * it is trying to conserve bandwidth because of bandwidth accounting rules + * ("soft hibernation"), or it is about to shut down ("exiting"). **/ /* @@ -49,8 +55,10 @@ typedef enum { UNIT_MONTH=1, UNIT_WEEK=2, UNIT_DAY=3, } time_unit_t; -/* Fields for accounting logic. Accounting overview: +/* + * @file hibernate.c * + * <h4>Accounting</h4> * Accounting is designed to ensure that no more than N bytes are sent in * either direction over a given interval (currently, one month, one week, or * one day) We could @@ -64,17 +72,21 @@ typedef enum { * * Each interval runs as follows: * - * 1. We guess our bandwidth usage, based on how much we used + * <ol> + * <li>We guess our bandwidth usage, based on how much we used * last time. We choose a "wakeup time" within the interval to come up. - * 2. Until the chosen wakeup time, we hibernate. - * 3. We come up at the wakeup time, and provide bandwidth until we are + * <li>Until the chosen wakeup time, we hibernate. + * <li> We come up at the wakeup time, and provide bandwidth until we are * "very close" to running out. - * 4. Then we go into low-bandwidth mode, and stop accepting new + * <li> Then we go into low-bandwidth mode, and stop accepting new * connections, but provide bandwidth until we run out. - * 5. Then we hibernate until the end of the interval. + * <li> Then we hibernate until the end of the interval. * * If the interval ends before we run out of bandwidth, we go back to * step one. + * + * Accounting is controlled by the AccountingMax, AccountingRule, and + * AccountingStart options. */ /** How many bytes have we read in this accounting interval? */ @@ -692,7 +704,7 @@ read_bandwidth_usage(void) int res; res = unlink(fname); - if (res != 0) { + if (res != 0 && errno != ENOENT) { log_warn(LD_FS, "Failed to unlink %s: %s", fname, strerror(errno)); diff --git a/src/or/include.am b/src/or/include.am index 02d67fa192..10f8b85bdf 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -64,6 +64,7 @@ LIBTOR_A_SOURCES = \ src/or/transports.c \ src/or/parsecommon.c \ src/or/periodic.c \ + src/or/protover.c \ src/or/policies.c \ src/or/reasons.c \ src/or/relay.c \ @@ -180,6 +181,7 @@ ORHEADERS = \ src/or/parsecommon.h \ src/or/periodic.h \ src/or/policies.h \ + src/or/protover.h \ src/or/reasons.h \ src/or/relay.h \ src/or/rendcache.h \ diff --git a/src/or/keypin.c b/src/or/keypin.c index 335c793cd9..2d4c4e92d2 100644 --- a/src/or/keypin.c +++ b/src/or/keypin.c @@ -39,16 +39,28 @@ * @brief Key-pinning for RSA and Ed25519 identity keys at directory * authorities. * + * Many older clients, and many internal interfaces, still refer to relays by + * their RSA1024 identity keys. We can make this more secure, however: + * authorities use this module to track which RSA keys have been used along + * with which Ed25519 keys, and force such associations to be permanent. + * * 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. * + * (As of this writing, these key associations are advisory only, mostly + * because some relay operators kept mishandling their Ed25519 keys during + * the initial Ed25519 rollout. We should fix this problem, and then toggle + * the AuthDirPinKeys option.) + * * 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. + * + * The dirserv.c module is the main user of these functions. */ static int keypin_journal_append_entry(const uint8_t *rsa_id_digest, diff --git a/src/or/main.c b/src/or/main.c index 6876236446..c10f62724a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -8,6 +8,42 @@ * \file main.c * \brief Toplevel module. Handles signals, multiplexes between * connections, implements main loop, and drives scheduled events. + * + * For the main loop itself; see run_main_loop_once(). It invokes the rest of + * Tor mostly through Libevent callbacks. Libevent callbacks can happen when + * a timer elapses, a signal is received, a socket is ready to read or write, + * or an event is manually activated. + * + * Most events in Tor are driven from these callbacks: + * <ul> + * <li>conn_read_callback() and conn_write_callback() here, which are + * invoked when a socket is ready to read or write respectively. + * <li>signal_callback(), which handles incoming signals. + * </ul> + * Other events are used for specific purposes, or for building more complex + * control structures. If you search for usage of tor_libevent_new(), you + * will find all the events that we construct in Tor. + * + * Tor has numerous housekeeping operations that need to happen + * regularly. They are handled in different ways: + * <ul> + * <li>The most frequent operations are handled after every read or write + * event, at the end of connection_handle_read() and + * connection_handle_write(). + * + * <li>The next most frequent operations happen after each invocation of the + * main loop, in run_main_loop_once(). + * + * <li>Once per second, we run all of the operations listed in + * second_elapsed_callback(), and in its child, run_scheduled_events(). + * + * <li>Once-a-second operations are handled in second_elapsed_callback(). + * + * <li>More infrequent operations take place based on the periodic event + * driver in periodic.c . These are stored in the periodic_events[] + * table. + * </ul> + * **/ #define MAIN_PRIVATE @@ -47,6 +83,7 @@ #include "onion.h" #include "periodic.h" #include "policies.h" +#include "protover.h" #include "transports.h" #include "relay.h" #include "rendclient.h" @@ -591,6 +628,19 @@ connection_should_read_from_linked_conn(connection_t *conn) return 0; } +/** If we called event_base_loop() and told it to never stop until it + * runs out of events, now we've changed our mind: tell it we want it to + * finish. */ +void +tell_event_loop_to_finish(void) +{ + if (!called_loop_once) { + struct timeval tv = { 0, 0 }; + tor_event_base_loopexit(tor_libevent_get_base(), &tv); + called_loop_once = 1; /* hack to avoid adding more exit events */ + } +} + /** Helper: Tell the main loop to begin reading bytes into <b>conn</b> from * its linked connection, if it is not doing so already. Called by * connection_start_reading and connection_start_writing as appropriate. */ @@ -603,14 +653,10 @@ connection_start_reading_from_linked_conn(connection_t *conn) if (!conn->active_on_link) { conn->active_on_link = 1; smartlist_add(active_linked_connection_lst, conn); - if (!called_loop_once) { - /* This is the first event on the list; we won't be in LOOP_ONCE mode, - * so we need to make sure that the event_base_loop() actually exits at - * the end of its run through the current connections and lets us - * activate read events for linked connections. */ - struct timeval tv = { 0, 0 }; - tor_event_base_loopexit(tor_libevent_get_base(), &tv); - } + /* make sure that the event_base_loop() function exits at + * the end of its run through the current connections, so we can + * activate read events for linked connections. */ + tell_event_loop_to_finish(); } else { tor_assert(smartlist_contains(active_linked_connection_lst, conn)); } @@ -1373,6 +1419,12 @@ run_scheduled_events(time_t now) circuit_expire_old_circs_as_needed(now); } + if (!net_is_disabled()) { + /* This is usually redundant with circuit_build_needed_circs() above, + * but it is very fast when there is no work to do. */ + connection_ap_attach_pending(0); + } + /* 5. We do housekeeping for each connection... */ connection_or_set_bad_connections(NULL, 0); int i; @@ -1408,13 +1460,13 @@ run_scheduled_events(time_t now) pt_configure_remaining_proxies(); } +/* Periodic callback: Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion + * keys, shut down and restart all cpuworkers, and update our descriptor if + * necessary. + */ 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 (server_mode(options)) { time_t rotation_time = get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME; if (rotation_time > now) { @@ -1434,6 +1486,9 @@ rotate_onion_key_callback(time_t now, const or_options_t *options) return PERIODIC_EVENT_NO_UPDATE; } +/* Periodic callback: Every 30 seconds, check whether it's time to make new + * Ed25519 subkeys. + */ static int check_ed_keys_callback(time_t now, const or_options_t *options) { @@ -1451,6 +1506,11 @@ check_ed_keys_callback(time_t now, const or_options_t *options) return PERIODIC_EVENT_NO_UPDATE; } +/** + * Periodic callback: Every {LAZY,GREEDY}_DESCRIPTOR_RETRY_INTERVAL, + * see about fetching descriptors, microdescriptors, and extrainfo + * documents. + */ static int launch_descriptor_fetches_callback(time_t now, const or_options_t *options) { @@ -1465,6 +1525,10 @@ launch_descriptor_fetches_callback(time_t now, const or_options_t *options) return GREEDY_DESCRIPTOR_RETRY_INTERVAL; } +/** + * Periodic event: Rotate our X.509 certificates and TLS keys once every + * MAX_SSL_KEY_LIFETIME_INTERNAL. + */ static int rotate_x509_certificate_callback(time_t now, const or_options_t *options) { @@ -1490,6 +1554,10 @@ rotate_x509_certificate_callback(time_t now, const or_options_t *options) return MAX_SSL_KEY_LIFETIME_INTERNAL; } +/** + * Periodic callback: once an hour, grab some more entropy from the + * kernel and feed it to our CSPRNG. + **/ static int add_entropy_callback(time_t now, const or_options_t *options) { @@ -1506,6 +1574,10 @@ add_entropy_callback(time_t now, const or_options_t *options) return ENTROPY_INTERVAL; } +/** + * Periodic callback: if we're an authority, make sure we test + * the routers on the network for reachability. + */ static int launch_reachability_tests_callback(time_t now, const or_options_t *options) { @@ -1517,6 +1589,10 @@ launch_reachability_tests_callback(time_t now, const or_options_t *options) return REACHABILITY_TEST_INTERVAL; } +/** + * Periodic callback: if we're an authority, discount the stability + * information (and other rephist information) that's older. + */ static int downrate_stability_callback(time_t now, const or_options_t *options) { @@ -1528,6 +1604,10 @@ downrate_stability_callback(time_t now, const or_options_t *options) return safe_timer_diff(now, next); } +/** + * Periodic callback: if we're an authority, record our measured stability + * information from rephist in an mtbf file. + */ static int save_stability_callback(time_t now, const or_options_t *options) { @@ -1540,6 +1620,10 @@ save_stability_callback(time_t now, const or_options_t *options) return SAVE_STABILITY_INTERVAL; } +/** + * Periodic callback: if we're an authority, check on our authority + * certificate (the one that authenticates our authority signing key). + */ static int check_authority_cert_callback(time_t now, const or_options_t *options) { @@ -1552,6 +1636,10 @@ check_authority_cert_callback(time_t now, const or_options_t *options) return CHECK_V3_CERTIFICATE_INTERVAL; } +/** + * Periodic callback: If our consensus is too old, recalculate whether + * we can actually use it. + */ static int check_expired_networkstatus_callback(time_t now, const or_options_t *options) { @@ -1571,6 +1659,9 @@ check_expired_networkstatus_callback(time_t now, const or_options_t *options) return CHECK_EXPIRED_NS_INTERVAL; } +/** + * Periodic callback: Write statistics to disk if appropriate. + */ static int write_stats_file_callback(time_t now, const or_options_t *options) { @@ -1618,6 +1709,9 @@ write_stats_file_callback(time_t now, const or_options_t *options) return safe_timer_diff(now, next_time_to_write_stats_files); } +/** + * Periodic callback: Write bridge statistics to disk if appropriate. + */ static int record_bridge_stats_callback(time_t now, const or_options_t *options) { @@ -1645,6 +1739,9 @@ record_bridge_stats_callback(time_t now, const or_options_t *options) return PERIODIC_EVENT_NO_UPDATE; } +/** + * Periodic callback: Clean in-memory caches every once in a while + */ static int clean_caches_callback(time_t now, const or_options_t *options) { @@ -1658,6 +1755,10 @@ clean_caches_callback(time_t now, const or_options_t *options) return CLEAN_CACHES_INTERVAL; } +/** + * Periodic callback: Clean the cache of failed hidden service lookups + * frequently frequently. + */ static int rend_cache_failure_clean_callback(time_t now, const or_options_t *options) { @@ -1669,20 +1770,21 @@ rend_cache_failure_clean_callback(time_t now, const or_options_t *options) return 30; } +/** + * Periodic callback: If we're a server and initializing dns failed, retry. + */ 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 (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). */ - +/** Periodic callback: consider rebuilding or and re-uploading our descriptor + * (if we've passed our internal checks). */ static int check_descriptor_callback(time_t now, const or_options_t *options) { @@ -1709,6 +1811,11 @@ check_descriptor_callback(time_t now, const or_options_t *options) return CHECK_DESCRIPTOR_INTERVAL; } +/** + * Periodic callback: check whether we're reachable (as a relay), and + * whether our bandwidth has changed enough that we need to + * publish a new descriptor. + */ static int check_for_reachability_bw_callback(time_t now, const or_options_t *options) { @@ -1745,13 +1852,13 @@ check_for_reachability_bw_callback(time_t now, const or_options_t *options) return CHECK_DESCRIPTOR_INTERVAL; } +/** + * Periodic event: once a minute, (or every second if TestingTorNetwork, or + * during client bootstrap), check whether we want to download any + * networkstatus documents. */ 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. */ - /* How often do we check whether we should download network status * documents? */ const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping( @@ -1773,12 +1880,13 @@ fetch_networkstatus_callback(time_t now, const or_options_t *options) return networkstatus_dl_check_interval; } +/** + * Periodic callback: Every 60 seconds, we relaunch listeners if any died. */ 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()) { retry_all_listeners(NULL, NULL, 0); return 60; @@ -1786,6 +1894,9 @@ retry_listeners_callback(time_t now, const or_options_t *options) return PERIODIC_EVENT_NO_UPDATE; } +/** + * Periodic callback: as a server, see if we have any old unused circuits + * that should be expired */ static int expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options) { @@ -1795,6 +1906,10 @@ expire_old_ciruits_serverside_callback(time_t now, const or_options_t *options) return 11; } +/** + * Periodic event: if we're an exit, see if our DNS server is telling us + * obvious lies. + */ static int check_dns_honesty_callback(time_t now, const or_options_t *options) { @@ -1817,6 +1932,10 @@ check_dns_honesty_callback(time_t now, const or_options_t *options) return 12*3600 + crypto_rand_int(12*3600); } +/** + * Periodic callback: if we're the bridge authority, write a networkstatus + * file to disk. + */ static int write_bridge_ns_callback(time_t now, const or_options_t *options) { @@ -1829,6 +1948,9 @@ write_bridge_ns_callback(time_t now, const or_options_t *options) return PERIODIC_EVENT_NO_UPDATE; } +/** + * Periodic callback: poke the tor-fw-helper app if we're using one. + */ static int check_fw_helper_app_callback(time_t now, const or_options_t *options) { @@ -1852,7 +1974,9 @@ check_fw_helper_app_callback(time_t now, const or_options_t *options) return PORT_FORWARDING_CHECK_INTERVAL; } -/** Callback to write heartbeat message in the logs. */ +/** + * Periodic callback: write the heartbeat message in the logs. + */ static int heartbeat_callback(time_t now, const or_options_t *options) { @@ -2358,19 +2482,26 @@ run_main_loop_once(void) /* Make it easier to tell whether libevent failure is our fault or not. */ errno = 0; #endif - /* All active linked conns should get their read events activated. */ + + /* All active linked conns should get their read events activated, + * so that libevent knows to run their callbacks. */ SMARTLIST_FOREACH(active_linked_connection_lst, connection_t *, conn, event_active(conn->read_event, EV_READ, 1)); called_loop_once = smartlist_len(active_linked_connection_lst) ? 1 : 0; + /* Make sure we know (about) what time it is. */ update_approx_time(time(NULL)); - /* poll until we have an event, or the second ends, or until we have - * some active linked connections to trigger events for. */ + /* Here it is: the main loop. Here we tell Libevent to poll until we have + * an event, or the second ends, or until we have some active linked + * connections to trigger events for. Libevent will wait till one + * of these happens, then run all the appropriate callbacks. */ loop_result = event_base_loop(tor_libevent_get_base(), called_loop_once ? EVLOOP_ONCE : 0); - /* let catch() handle things like ^c, and otherwise don't worry about it */ + /* Oh, the loop failed. That might be an error that we need to + * catch, but more likely, it's just an interrupted poll() call or something, + * and we should try again. */ if (loop_result < 0) { int e = tor_socket_errno(-1); /* let the program survive things like ^z */ @@ -2393,9 +2524,17 @@ 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. */ + /* And here is where we put callbacks that happen "every time the event loop + * runs." They must be very fast, or else the whole Tor process will get + * slowed down. + * + * 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. */ + + /* If there are any pending client connections, try attaching them to + * circuits (if we can.) This will be pretty fast if nothing new is + * pending. + */ connection_ap_attach_pending(0); return 1; @@ -2834,11 +2973,6 @@ tor_init(int argc, char *argv[]) "Expect more bugs than usual."); } -#ifdef NON_ANONYMOUS_MODE_ENABLED - log_warn(LD_GENERAL, "This copy of Tor was compiled to run in a " - "non-anonymous mode. It will provide NO ANONYMITY."); -#endif - if (network_init()<0) { log_err(LD_BUG,"Error initializing network; exiting."); return -1; @@ -2850,15 +2984,18 @@ tor_init(int argc, char *argv[]) return -1; } + /* The options are now initialised */ + const or_options_t *options = get_options(); + #ifndef _WIN32 if (geteuid()==0) log_warn(LD_GENERAL,"You are running Tor as root. You don't need to, " "and you probably shouldn't."); #endif - if (crypto_global_init(get_options()->HardwareAccel, - get_options()->AccelName, - get_options()->AccelDir)) { + if (crypto_global_init(options->HardwareAccel, + options->AccelName, + options->AccelDir)) { log_err(LD_BUG, "Unable to initialize OpenSSL. Exiting."); return -1; } @@ -2976,6 +3113,7 @@ tor_free_all(int postfork) ext_orport_free_all(); control_free_all(); sandbox_free_getaddrinfo_cache(); + protover_free_all(); if (!postfork) { config_free_all(); or_state_free_all(); diff --git a/src/or/main.h b/src/or/main.h index 0220ae3c57..07b22598b1 100644 --- a/src/or/main.h +++ b/src/or/main.h @@ -45,6 +45,8 @@ int connection_is_writing(connection_t *conn); MOCK_DECL(void,connection_stop_writing,(connection_t *conn)); MOCK_DECL(void,connection_start_writing,(connection_t *conn)); +void tell_event_loop_to_finish(void); + void connection_stop_reading_from_linked_conn(connection_t *conn); MOCK_DECL(int, connection_count_moribund, (void)); diff --git a/src/or/microdesc.c b/src/or/microdesc.c index a81dc54628..140117f683 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -917,20 +917,9 @@ update_microdescs_from_networkstatus(time_t now) int we_use_microdescriptors_for_circuits(const or_options_t *options) { - int ret = options->UseMicrodescriptors; - if (ret == -1) { - /* UseMicrodescriptors is "auto"; we need to decide: */ - /* If we are configured to use bridges and none of our bridges - * know what a microdescriptor is, the answer is no. */ - if (options->UseBridges && !any_bridge_supports_microdescriptors()) - return 0; - /* Otherwise, we decide that we'll use microdescriptors iff we are - * not a server, and we're not autofetching everything. */ - /* XXXX++ what does not being a server have to do with it? also there's - * a partitioning issue here where bridges differ from clients. */ - ret = !server_mode(options) && !options->FetchUselessDescriptors; - } - return ret; + if (options->UseMicrodescriptors == 0) + return 0; /* the user explicitly picked no */ + return 1; /* yes and auto both mean yes */ } /** Return true iff we should try to download microdescriptors at all. */ diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index fe4b4562ff..ed888fb53e 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -6,8 +6,34 @@ /** * \file networkstatus.c - * \brief Functions and structures for handling network status documents as a - * client or cache. + * \brief Functions and structures for handling networkstatus documents as a + * client or as a directory cache. + * + * A consensus networkstatus object is created by the directory + * authorities. It authenticates a set of network parameters--most + * importantly, the list of all the relays in the network. This list + * of relays is represented as an array of routerstatus_t objects. + * + * There are currently two flavors of consensus. With the older "NS" + * flavor, each relay is associated with a digest of its router + * descriptor. Tor instances that use this consensus keep the list of + * router descriptors as routerinfo_t objects stored and managed in + * routerlist.c. With the newer "microdesc" flavor, each relay is + * associated with a digest of the microdescriptor that the authorities + * made for it. These are stored and managed in microdesc.c. Information + * about the router is divided between the the networkstatus and the + * microdescriptor according to the general rule that microdescriptors + * should hold information that changes much less frequently than the + * information in the networkstatus. + * + * Modern clients use microdescriptor networkstatuses. Directory caches + * need to keep both kinds of networkstatus document, so they can serve them. + * + * This module manages fetching, holding, storing, updating, and + * validating networkstatus objects. The download-and-validate process + * is slightly complicated by the fact that the keys you need to + * validate a consensus are stored in the authority certificates, which + * you might not have yet when you download the consensus. */ #define NETWORKSTATUS_PRIVATE @@ -28,6 +54,7 @@ #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" +#include "protover.h" #include "relay.h" #include "router.h" #include "routerlist.h" @@ -42,14 +69,6 @@ static strmap_t *named_server_map = NULL; * as unnamed for some server in the consensus. */ static strmap_t *unnamed_server_map = NULL; -/** Most recently received and validated v3 consensus network status, - * of whichever type we are using for our own circuits. This will be the same - * as one of current_ns_consensus or current_md_consensus. - */ -#define current_consensus \ - (we_use_microdescriptors_for_circuits(get_options()) ? \ - current_md_consensus : current_ns_consensus) - /** Most recently received and validated v3 "ns"-flavored consensus network * status. */ static networkstatus_t *current_ns_consensus = NULL; @@ -124,16 +143,17 @@ static void routerstatus_list_update_named_server_map(void); static void update_consensus_bootstrap_multiple_downloads( time_t now, const or_options_t *options); +static int networkstatus_check_required_protocols(const networkstatus_t *ns, + int client_mode, + char **warning_out); /** Forget that we've warned about anything networkstatus-related, so we will * give fresh warnings if the same behavior happens again. */ void networkstatus_reset_warnings(void) { - if (current_consensus) { - SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, - node->name_lookup_warned = 0); - } + SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, + node->name_lookup_warned = 0); have_warned_about_old_version = 0; have_warned_about_new_version = 0; @@ -206,7 +226,7 @@ router_reload_consensus_networkstatus(void) tor_free(filename); } - if (!current_consensus) { + if (!networkstatus_get_latest_consensus()) { if (!named_server_map) named_server_map = strmap_new(); if (!unnamed_server_map) @@ -229,6 +249,7 @@ vote_routerstatus_free(vote_routerstatus_t *rs) if (!rs) return; tor_free(rs->version); + tor_free(rs->protocols); tor_free(rs->status.exitsummary); for (h = rs->microdesc; h; h = next) { tor_free(h->microdesc_hash_line); @@ -275,6 +296,11 @@ networkstatus_vote_free(networkstatus_t *ns) tor_free(ns->client_versions); tor_free(ns->server_versions); + tor_free(ns->recommended_client_protocols); + tor_free(ns->recommended_relay_protocols); + tor_free(ns->required_client_protocols); + tor_free(ns->required_relay_protocols); + if (ns->known_flags) { SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c)); smartlist_free(ns->known_flags); @@ -647,7 +673,7 @@ router_get_mutable_consensus_status_by_descriptor_digest,( const char *digest)) { if (!consensus) - consensus = current_consensus; + consensus = networkstatus_get_latest_consensus(); if (!consensus) return NULL; if (!consensus->desc_digest_map) { @@ -728,9 +754,11 @@ router_get_dl_status_by_descriptor_digest,(const char *d)) routerstatus_t * router_get_mutable_consensus_status_by_id(const char *digest) { - if (!current_consensus) + const networkstatus_t *ns = networkstatus_get_latest_consensus(); + if (!ns) return NULL; - return smartlist_bsearch(current_consensus->routerstatus_list, digest, + smartlist_t *rslist = ns->routerstatus_list; + return smartlist_bsearch(rslist, digest, compare_digest_to_routerstatus_entry); } @@ -1280,7 +1308,10 @@ networkstatus_get_dl_status_by_flavor_running,(consensus_flavor_t flavor)) MOCK_IMPL(networkstatus_t *, networkstatus_get_latest_consensus,(void)) { - return current_consensus; + if (we_use_microdescriptors_for_circuits(get_options())) + return current_md_consensus; + else + return current_ns_consensus; } /** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL @@ -1303,10 +1334,10 @@ networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f)) MOCK_IMPL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now)) { - if (current_consensus && - current_consensus->valid_after <= now && - now <= current_consensus->valid_until) - return current_consensus; + if (networkstatus_get_latest_consensus() && + networkstatus_get_latest_consensus()->valid_after <= now && + now <= networkstatus_get_latest_consensus()->valid_until) + return networkstatus_get_latest_consensus(); else return NULL; } @@ -1446,8 +1477,9 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->is_valid != b->is_valid || a->is_possible_guard != b->is_possible_guard || a->is_bad_exit != b->is_bad_exit || - a->is_hs_dir != b->is_hs_dir || - a->version_known != b->version_known; + a->is_hs_dir != b->is_hs_dir; + // XXXX this function needs a huge refactoring; it has gotten out + // XXXX of sync with routerstatus_t, and it will do so again. } /** Notify controllers of any router status entries that changed between @@ -1546,6 +1578,66 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, } #endif //TOR_UNIT_TESTS +/** + * Return true if any option is set in <b>options</b> to make us behave + * as a client. + * + * XXXX If we need this elsewhere at any point, we should make it nonstatic + * XXXX and move it into another file. + */ +static int +any_client_port_set(const or_options_t *options) +{ + return (options->SocksPort_set || + options->TransPort_set || + options->NATDPort_set || + options->ControlPort_set || + options->DNSPort_set); +} + +/** + * Helper for handle_missing_protocol_warning: handles either the + * client case (if <b>is_client</b> is set) or the server case otherwise. + */ +static void +handle_missing_protocol_warning_impl(const networkstatus_t *c, + int is_client) +{ + char *protocol_warning = NULL; + + int should_exit = networkstatus_check_required_protocols(c, + is_client, + &protocol_warning); + if (protocol_warning) { + tor_log(should_exit ? LOG_ERR : LOG_WARN, + LD_GENERAL, + "%s", protocol_warning); + } + if (should_exit) { + tor_assert_nonfatal(protocol_warning); + } + tor_free(protocol_warning); + if (should_exit) + exit(1); +} + +/** Called when we have received a networkstatus <b>c</b>. If there are + * any _required_ protocols we are missing, log an error and exit + * immediately. If there are any _recommended_ protocols we are missing, + * warn. */ +static void +handle_missing_protocol_warning(const networkstatus_t *c, + const or_options_t *options) +{ + const int is_server = server_mode(options); + const int is_client = any_client_port_set(options) || !is_server; + + if (is_server) + handle_missing_protocol_warning_impl(c, 0); + if (is_client) + handle_missing_protocol_warning_impl(c, 1); +} + /** 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. @@ -1589,6 +1681,7 @@ networkstatus_set_current_consensus(const char *consensus, time_t current_valid_after = 0; int free_consensus = 1; /* Free 'c' at the end of the function */ int old_ewma_enabled; + int checked_protocols_already = 0; if (flav < 0) { /* XXXX we don't handle unrecognized flavors yet. */ @@ -1604,6 +1697,16 @@ networkstatus_set_current_consensus(const char *consensus, goto done; } + if (from_cache && !was_waiting_for_certs) { + /* We previously stored this; check _now_ to make sure that version-kills + * really work. This happens even before we check signatures: we did so + * before when we stored this to disk. This does mean an attacker who can + * write to the datadir can make us not start: such an attacker could + * already harm us by replacing our guards, which would be worse. */ + checked_protocols_already = 1; + handle_missing_protocol_warning(c, options); + } + if ((int)c->flavor != flav) { /* This wasn't the flavor we thought we were getting. */ if (require_flavor) { @@ -1729,18 +1832,25 @@ networkstatus_set_current_consensus(const char *consensus, if (!from_cache && flav == usable_consensus_flavor()) control_event_client_status(LOG_NOTICE, "CONSENSUS_ARRIVED"); + if (!checked_protocols_already) { + handle_missing_protocol_warning(c, options); + } + /* Are we missing any certificates at all? */ if (r != 1 && dl_certs) authority_certs_fetch_missing(c, now, source_dir); - if (flav == usable_consensus_flavor()) { - notify_control_networkstatus_changed(current_consensus, c); + const int is_usable_flavor = flav == usable_consensus_flavor(); + + if (is_usable_flavor) { + notify_control_networkstatus_changed( + networkstatus_get_latest_consensus(), c); } if (flav == FLAV_NS) { if (current_ns_consensus) { networkstatus_copy_old_consensus_info(c, current_ns_consensus); networkstatus_vote_free(current_ns_consensus); - /* Defensive programming : we should set current_consensus very soon, + /* Defensive programming : we should set current_ns_consensus very soon * but we're about to call some stuff in the meantime, and leaving this * dangling pointer around has proven to be trouble. */ current_ns_consensus = NULL; @@ -1776,27 +1886,19 @@ 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(); /* Update ewma and adjust policy if needed; first cache the old value */ old_ewma_enabled = cell_ewma_enabled(); /* Change the cell EWMA settings */ - cell_ewma_set_scale_factor(options, networkstatus_get_latest_consensus()); + cell_ewma_set_scale_factor(options, c); /* If we just enabled ewma, set the cmux policy on all active channels */ if (cell_ewma_enabled() && !old_ewma_enabled) { channel_set_cmux_policy_everywhere(&ewma_policy); @@ -1809,8 +1911,16 @@ networkstatus_set_current_consensus(const char *consensus, * current consensus really alter our view of any OR's rate limits? */ connection_or_update_token_buckets(get_connection_array(), options); - circuit_build_times_new_consensus_params(get_circuit_build_times_mutable(), - current_consensus); + circuit_build_times_new_consensus_params( + get_circuit_build_times_mutable(), c); + } + + /* 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)) { @@ -1952,15 +2062,16 @@ routers_update_all_from_networkstatus(time_t now, int dir_version) static void routerstatus_list_update_named_server_map(void) { - if (!current_consensus) + networkstatus_t *ns = networkstatus_get_latest_consensus(); + if (!ns) return; strmap_free(named_server_map, tor_free_); named_server_map = strmap_new(); strmap_free(unnamed_server_map, NULL); unnamed_server_map = strmap_new(); - SMARTLIST_FOREACH_BEGIN(current_consensus->routerstatus_list, - const routerstatus_t *, rs) { + smartlist_t *rslist = ns->routerstatus_list; + SMARTLIST_FOREACH_BEGIN(rslist, const routerstatus_t *, rs) { if (rs->is_named) { strmap_set_lc(named_server_map, rs->nickname, tor_memdup(rs->identity_digest, DIGEST_LEN)); @@ -1980,7 +2091,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers, { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); - networkstatus_t *ns = current_consensus; + networkstatus_t *ns = networkstatus_get_latest_consensus(); if (!ns || !smartlist_len(ns->routerstatus_list)) return; @@ -2049,7 +2160,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs) char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { - return routerstatus_format_entry(rs, NULL, NS_CONTROL_PORT, NULL); + return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, NULL); } /** Alloc and return a string describing routerstatuses for the most @@ -2275,6 +2386,12 @@ client_would_use_router(const routerstatus_t *rs, time_t now, /* We'd drop it immediately for being too old. */ return 0; } + if (!routerstatus_version_supports_extend2_cells(rs, 1)) { + /* We'd ignore it because it doesn't support EXTEND2 cells. + * If we don't know the version, download the descriptor so we can + * check if it supports EXTEND2 cells and ntor. */ + return 0; + } return 1; } @@ -2290,14 +2407,14 @@ getinfo_helper_networkstatus(control_connection_t *conn, const routerstatus_t *status; (void) conn; - if (!current_consensus) { + if (!networkstatus_get_latest_consensus()) { *answer = tor_strdup(""); return 0; } if (!strcmp(question, "ns/all")) { smartlist_t *statuses = smartlist_new(); - SMARTLIST_FOREACH(current_consensus->routerstatus_list, + SMARTLIST_FOREACH(networkstatus_get_latest_consensus()->routerstatus_list, const routerstatus_t *, rs, { smartlist_add(statuses, networkstatus_getinfo_helper_single(rs)); @@ -2358,6 +2475,56 @@ getinfo_helper_networkstatus(control_connection_t *conn, return 0; } +/** Check whether the networkstatus <b>ns</b> lists any protocol + * versions as "required" or "recommended" that we do not support. If + * so, set *<b>warning_out</b> to a newly allocated string describing + * the problem. + * + * Return 1 if we should exit, 0 if we should not. */ +int +networkstatus_check_required_protocols(const networkstatus_t *ns, + int client_mode, + char **warning_out) +{ + const char *func = client_mode ? "client" : "relay"; + const char *required, *recommended; + char *missing = NULL; + + tor_assert(warning_out); + + if (client_mode) { + required = ns->required_client_protocols; + recommended = ns->recommended_client_protocols; + } else { + required = ns->required_relay_protocols; + recommended = ns->recommended_relay_protocols; + } + + if (!protover_all_supported(required, &missing)) { + tor_asprintf(warning_out, "At least one protocol listed as required in " + "the consensus is not supported by this version of Tor. " + "You should upgrade. This version of Tor will not work as a " + "%s on the Tor network. The missing protocols are: %s", + func, missing); + tor_free(missing); + return 1; + } + + if (! protover_all_supported(recommended, &missing)) { + tor_asprintf(warning_out, "At least one protocol listed as recommended in " + "the consensus is not supported by this version of Tor. " + "You should upgrade. This version of Tor will eventually " + "stop working as a %s on the Tor network. The missing " + "protocols are: %s", + func, missing); + tor_free(missing); + } + + tor_assert_nonfatal(missing == NULL); + + return 0; +} + /** Free all storage held locally in this module. */ void networkstatus_free_all(void) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 7b64cafd79..2802d5b9e0 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -10,6 +10,32 @@ * \brief Structures and functions for tracking what we know about the routers * on the Tor network, and correlating information from networkstatus, * routerinfo, and microdescs. + * + * The key structure here is node_t: that's the canonical way to refer + * to a Tor relay that we might want to build a circuit through. Every + * node_t has either a routerinfo_t, or a routerstatus_t from the current + * networkstatus consensus. If it has a routerstatus_t, it will also + * need to have a microdesc_t before you can use it for circuits. + * + * The nodelist_t is a global singleton that maps identities to node_t + * objects. Access them with the node_get_*() functions. The nodelist_t + * is maintained by calls throughout the codebase + * + * Generally, other code should not have to reach inside a node_t to + * see what information it has. Instead, you should call one of the + * many accessor functions that works on a generic node_t. If there + * isn't one that does what you need, it's better to make such a function, + * and then use it. + * + * For historical reasons, some of the functions that select a node_t + * from the list of all usable node_t objects are in the routerlist.c + * module, since they originally selected a routerinfo_t. (TODO: They + * should move!) + * + * (TODO: Perhaps someday we should abstract the remaining ways of + * talking about a relay to also be node_t instances. Those would be + * routerstatus_t as used for directory requests, and dir_server_t as + * used for authorities and fallback directories.) */ #include "or.h" @@ -1173,14 +1199,38 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) } } +/** Return true iff <b>md</b> has a curve25519 onion key. + * Use node_has_curve25519_onion_key() instead of calling this directly. */ +static int +microdesc_has_curve25519_onion_key(const microdesc_t *md) +{ + if (!md) { + return 0; + } + + if (!md->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + /** Return true iff <b>node</b> has a curve25519 onion key. */ int node_has_curve25519_onion_key(const node_t *node) { + if (!node) + return 0; + if (node->ri) - return node->ri->onion_curve25519_pkey != NULL; + return routerinfo_has_curve25519_onion_key(node->ri); else if (node->md) - return node->md->onion_curve25519_pkey != NULL; + return microdesc_has_curve25519_onion_key(node->md); else return 0; } diff --git a/src/or/ntmain.c b/src/or/ntmain.c index a1b886bb5a..4c65805b32 100644 --- a/src/or/ntmain.c +++ b/src/or/ntmain.c @@ -6,7 +6,15 @@ /** * \file ntmain.c * - * \brief Entry points for running/configuring Tor as Windows Service. + * \brief Entry points for running/configuring Tor as a Windows Service. + * + * Windows Services expect to be registered with the operating system, and to + * have entry points for starting, stopping, and monitoring them. This module + * implements those entry points so that a tor relay or client or hidden + * service can run as a Windows service. Therefore, this module + * is only compiled when building for Windows. + * + * Warning: this module is not very well tested or very well maintained. */ #ifdef _WIN32 diff --git a/src/or/onion.c b/src/or/onion.c index 5495074a83..a987883802 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -8,9 +8,62 @@ * \file onion.c * \brief Functions to queue create cells, wrap the various onionskin types, * and parse and create the CREATE cell and its allies. + * + * This module has a few functions, all related to the CREATE/CREATED + * handshake that we use on links in order to create a circuit, and the + * related EXTEND/EXTENDED handshake that we use over circuits in order to + * extend them an additional hop. + * + * In this module, we provide a set of abstractions to create a uniform + * interface over the three circuit extension handshakes that Tor has used + * over the years (TAP, CREATE_FAST, and ntor). These handshakes are + * implemented in onion_tap.c, onion_fast.c, and onion_ntor.c respectively. + * + * All[*] of these handshakes follow a similar pattern: a client, knowing + * some key from the relay it wants to extend through, generates the + * first part of a handshake. A relay receives that handshake, and sends + * a reply. Once the client handles the reply, it knows that it is + * talking to the right relay, and it shares some freshly negotiated key + * material with that relay. + * + * We sometimes call the client's part of the handshake an "onionskin". + * We do this because historically, Onion Routing used a multi-layer + * structure called an "onion" to construct circuits. Each layer of the + * onion contained key material chosen by the client, the identity of + * the next relay in the circuit, and a smaller onion, encrypted with + * the key of the next relay. When we changed Tor to use a telescoping + * circuit extension design, it corresponded to sending each layer of the + * onion separately -- as a series of onionskins. + * + * Clients invoke these functions when creating or extending a circuit, + * from circuitbuild.c. + * + * Relays invoke these functions when they receive a CREATE or EXTEND + * cell in command.c or relay.c, in order to queue the pending request. + * They also invoke them from cpuworker.c, which handles dispatching + * onionskin requests to different worker threads. + * + * <br> + * + * This module also handles: + * <ul> + * <li> Queueing incoming onionskins on the relay side before passing + * them to worker threads. + * <li>Expiring onionskins on the relay side if they have waited for + * too long. + * <li>Packaging private keys on the server side in order to pass + * them to worker threads. + * <li>Encoding and decoding CREATE, CREATED, CREATE2, and CREATED2 cells. + * <li>Encoding and decodign EXTEND, EXTENDED, EXTEND2, and EXTENDED2 + * relay cells. + * </ul> + * + * [*] The CREATE_FAST handshake is weaker than described here; see + * onion_fast.c for more information. **/ #include "or.h" +#include "circuitbuild.h" #include "circuitlist.h" #include "config.h" #include "cpuworker.h" @@ -438,8 +491,7 @@ onion_skin_create(int type, r = CREATE_FAST_LEN; break; case ONION_HANDSHAKE_TYPE_NTOR: - if (tor_mem_is_zero((const char*)node->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN)) + if (!extend_info_supports_ntor(node)) return -1; if (onion_skin_ntor_create((const uint8_t*)node->identity_digest, &node->curve25519_onion_key, diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c index 6b5d12e407..8dcbfe22d8 100644 --- a/src/or/onion_fast.c +++ b/src/or/onion_fast.c @@ -7,6 +7,24 @@ /** * \file onion_fast.c * \brief Functions implement the CREATE_FAST circuit handshake. + * + * The "CREATE_FAST" handshake is an unauthenticated, non-forward-secure + * key derivation mechanism based on SHA1. We used to use it for the + * first hop of each circuit, since the TAP handshake provided no + * additional security beyond the security already provided by the TLS + * handshake [*]. + * + * When we switched to ntor, we deprecated CREATE_FAST, since ntor is + * stronger than our TLS handshake was, and fast enough to not be worrisome. + * + * This handshake, like the other circuit-extension handshakes, is + * invoked from onion.c. + * + * [*]Actually, it's possible that TAP _was_ a little better than TLS with + * RSA1024 certificates and EDH1024 for forward secrecy, if you + * hypothesize an adversary who can compute discrete logarithms on a + * small number of targetted DH1024 fields, but who can't break all that + * many RSA1024 keys. **/ #include "or.h" diff --git a/src/or/onion_ntor.c b/src/or/onion_ntor.c index d1a268f4cd..ded97ee73d 100644 --- a/src/or/onion_ntor.c +++ b/src/or/onion_ntor.c @@ -5,6 +5,17 @@ * \file onion_ntor.c * * \brief Implementation for the ntor handshake. + * + * The ntor circuit-extension handshake was developed as a replacement + * for the old TAP handshake. It uses Elliptic-curve Diffie-Hellman and + * a hash function in order to perform a one-way authenticated key + * exchange. The ntor handshake is meant to replace the old "TAP" + * handshake. + * + * We instantiate ntor with curve25519, HMAC-SHA256, and HKDF. + * + * This handshake, like the other circuit-extension handshakes, is + * invoked from onion.c. */ #include "orconfig.h" diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c index abe779351f..2769300945 100644 --- a/src/or/onion_tap.c +++ b/src/or/onion_tap.c @@ -9,10 +9,22 @@ * \brief Functions to implement the original Tor circuit extension handshake * (a.k.a TAP). * + * The "TAP" handshake is the first one that was widely used in Tor: It + * combines RSA1024-OAEP and AES128-CTR to perform a hybrid encryption over + * the first message DH1024 key exchange. (The RSA-encrypted part of the + * encryption is authenticated; the AES-encrypted part isn't. This was + * not a smart choice.) + * * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the * Security of the Tor Authentication Protocol". (Spoiler: it's secure, but * its security is kind of fragile and implementation dependent. Never modify * this implementation without reading and understanding that paper at least.) + * + * We have deprecated TAP since the ntor handshake came into general use. It + * is still used for hidden service IP and RP connections, however. + * + * This handshake, like the other circuit-extension handshakes, is + * invoked from onion.c. **/ #include "or.h" diff --git a/src/or/or.h b/src/or/or.h index 88f53637ed..a43e2a33cd 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1171,6 +1171,8 @@ typedef struct entry_port_cfg_t { unsigned int ipv4_traffic : 1; unsigned int ipv6_traffic : 1; unsigned int prefer_ipv6 : 1; + unsigned int dns_request : 1; + unsigned int onion_traffic : 1; /** For a socks listener: should we cache IPv4/IPv6 DNS information that * exit nodes tell us? @@ -1366,13 +1368,30 @@ typedef struct listener_connection_t { #define OR_CERT_TYPE_RSA_ED_CROSSCERT 7 /**@}*/ -/** The one currently supported type of AUTHENTICATE cell. It contains +/** The first supported type of AUTHENTICATE cell. It contains * a bunch of structures signed with an RSA1024 key. The signed * structures include a HMAC using negotiated TLS secrets, and a digest * of all cells sent or received before the AUTHENTICATE cell (including * the random server-generated AUTH_CHALLENGE cell). */ #define AUTHTYPE_RSA_SHA256_TLSSECRET 1 +/** As AUTHTYPE_RSA_SHA256_TLSSECRET, but instead of using the + * negotiated TLS secrets, uses exported keying material from the TLS + * session as described in RFC 5705. + * + * Not used by today's tors, since everything that supports this + * also supports ED25519_SHA3_5705, which is better. + **/ +#define AUTHTYPE_RSA_SHA256_RFC5705 2 +/** As AUTHTYPE_RSA_SHA256_RFC5705, but uses an Ed25519 identity key to + * authenticate. */ +#define AUTHTYPE_ED25519_SHA256_RFC5705 3 +/* + * NOTE: authchallenge_type_is_better() relies on these AUTHTYPE codes + * being sorted in order of preference. If we someday add one with + * a higher numerical value that we don't like as much, we should revise + * authchallenge_type_is_better(). + */ /** The length of the part of the AUTHENTICATE cell body that the client and * server can generate independently (when using RSA_SHA256_TLSSECRET). It @@ -1383,6 +1402,34 @@ typedef struct listener_connection_t { * signs. */ #define V3_AUTH_BODY_LEN (V3_AUTH_FIXED_PART_LEN + 8 + 16) +/** Structure to hold all the certificates we've received on an OR connection + */ +typedef struct or_handshake_certs_t { + /** True iff we originated this connection. */ + int started_here; + /** The cert for the 'auth' RSA key that's supposed to sign the AUTHENTICATE + * cell. Signed with the RSA identity key. */ + tor_x509_cert_t *auth_cert; + /** The cert for the 'link' RSA key that was used to negotiate the TLS + * connection. Signed with the RSA identity key. */ + tor_x509_cert_t *link_cert; + /** A self-signed identity certificate: the RSA identity key signed + * with itself. */ + tor_x509_cert_t *id_cert; + /** The Ed25519 signing key, signed with the Ed25519 identity key. */ + struct tor_cert_st *ed_id_sign; + /** A digest of the X509 link certificate for the TLS connection, signed + * with the Ed25519 siging key. */ + struct tor_cert_st *ed_sign_link; + /** The Ed25519 authentication key (that's supposed to sign an AUTHENTICATE + * cell) , signed with the Ed25519 siging key. */ + struct tor_cert_st *ed_sign_auth; + /** The Ed25519 identity key, crosssigned with the RSA identity key. */ + uint8_t *ed_rsa_crosscert; + /** The length of <b>ed_rsa_crosscert</b> in bytes */ + size_t ed_rsa_crosscert_len; +} or_handshake_certs_t; + /** Stores flags and information related to the portion of a v2/v3 Tor OR * connection handshake that happens after the TLS handshake is finished. */ @@ -1403,6 +1450,8 @@ typedef struct or_handshake_state_t { /* True iff we've received valid authentication to some identity. */ unsigned int authenticated : 1; + unsigned int authenticated_rsa : 1; + unsigned int authenticated_ed25519 : 1; /* True iff we have sent a netinfo cell */ unsigned int sent_netinfo : 1; @@ -1420,9 +1469,12 @@ typedef struct or_handshake_state_t { unsigned int digest_received_data : 1; /**@}*/ - /** Identity digest that we have received and authenticated for our peer + /** Identity RSA digest that we have received and authenticated for our peer * on this connection. */ - uint8_t authenticated_peer_id[DIGEST_LEN]; + uint8_t authenticated_rsa_peer_id[DIGEST_LEN]; + /** Identity Ed25519 public key that we have received and authenticated for + * our peer on this connection. */ + ed25519_public_key_t authenticated_ed25519_peer_id; /** Digests of the cells that we have sent or received as part of a V3 * handshake. Used for making and checking AUTHENTICATE cells. @@ -1435,14 +1487,8 @@ typedef struct or_handshake_state_t { /** Certificates that a connection initiator sent us in a CERTS cell; we're * holding on to them until we get an AUTHENTICATE cell. - * - * @{ */ - /** The cert for the key that's supposed to sign the AUTHENTICATE cell */ - tor_x509_cert_t *auth_cert; - /** A self-signed identity certificate */ - tor_x509_cert_t *id_cert; - /**@}*/ + or_handshake_certs_t *certs; } or_handshake_state_t; /** Length of Extended ORPort connection identifier. */ @@ -2091,6 +2137,9 @@ typedef struct { char *platform; /**< What software/operating system is this OR using? */ + char *protocol_list; /**< Encoded list of subprotocol versions supported + * by this OR */ + /* link info */ uint32_t bandwidthrate; /**< How many bytes does this OR add to its token * bucket per second? */ @@ -2208,14 +2257,13 @@ typedef struct routerstatus_t { 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 we have a proto line for this router, or a versions line + * from which we could infer the protocols. */ + unsigned int protocols_known:1; - /** True iff this router has a version that allows it to accept EXTEND2 - * cells */ - unsigned int version_supports_extend2_cells:1; + /** True iff this router has a version or protocol list that allows it to + * accept EXTEND2 cells */ + unsigned int supports_extend2_cells:1; unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */ unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */ @@ -2379,9 +2427,6 @@ typedef struct node_t { /** Local info: we treat this node as if it rejects everything */ unsigned int rejects_all:1; - /** Local info: this node is in our list of guards */ - unsigned int using_as_guard:1; - /* Local info: derived. */ /** True if the IPv6 OR port is preferred over the IPv4 OR port. @@ -2423,6 +2468,8 @@ typedef struct vote_routerstatus_t { * networkstatus_t.known_flags. */ char *version; /**< The version that the authority says this router is * running. */ + char *protocols; /**< The protocols that this authority says this router + * provides. */ 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. */ @@ -2540,6 +2587,16 @@ typedef struct networkstatus_t { * voter has no opinion. */ char *client_versions; char *server_versions; + + /** Lists of subprotocol versions which are _recommended_ for relays and + * clients, or which are _require_ for relays and clients. Tor shouldn't + * make any more network connections if a required protocol is missing. + */ + char *recommended_relay_protocols; + char *recommended_client_protocols; + char *required_relay_protocols; + char *required_client_protocols; + /** List of flags that this vote/consensus applies to routers. If a flag is * not listed here, the voter has no opinion on what its value should be. */ smartlist_t *known_flags; @@ -3622,9 +3679,13 @@ typedef struct { /** @name port booleans * - * Derived booleans: True iff there is a non-listener port on an AF_INET or - * AF_INET6 address of the given type configured in one of the _lines - * options above. + * Derived booleans: For server ports and ControlPort, true iff there is a + * non-listener port on an AF_INET or AF_INET6 address of the given type + * configured in one of the _lines options above. + * For client ports, also true if there is a unix socket configured. + * If you are checking for client ports, you may want to use: + * SocksPort_set || TransPort_set || NATDPort_set || DNSPort_set + * rather than SocksPort_set. * * @{ */ @@ -3715,6 +3776,26 @@ typedef struct { * they reach the normal circuit-build timeout. */ int CloseHSServiceRendCircuitsImmediatelyOnTimeout; + /** Onion Services in HiddenServiceSingleHopMode make one-hop (direct) + * circuits between the onion service server, and the introduction and + * rendezvous points. (Onion service descriptors are still posted using + * 3-hop paths, to avoid onion service directories blocking the service.) + * This option makes every hidden service instance hosted by + * this tor instance a Single Onion Service. + * HiddenServiceSingleHopMode requires HiddenServiceNonAnonymousMode to be + * set to 1. + * Use rend_service_allow_non_anonymous_connection() or + * rend_service_reveal_startup_time() instead of using this option directly. + */ + int HiddenServiceSingleHopMode; + /* Makes hidden service clients and servers non-anonymous on this tor + * instance. Allows the non-anonymous HiddenServiceSingleHopMode. Enables + * non-anonymous behaviour in the hidden service protocol. + * Use rend_service_non_anonymous_mode_enabled() instead of using this option + * directly. + */ + int HiddenServiceNonAnonymousMode; + int ConnLimit; /**< Demanded minimum number of simultaneous connections. */ int ConnLimit_; /**< Maximum allowed number of simultaneous connections. */ int ConnLimit_high_thresh; /**< start trying to lower socket usage if we @@ -3770,7 +3851,8 @@ typedef struct { * unattached before we fail it? */ int LearnCircuitBuildTimeout; /**< If non-zero, we attempt to learn a value * for CircuitBuildTimeout based on timeout - * history */ + * history. Use circuit_build_times_disabled() + * rather than checking this value directly. */ int CircuitBuildTimeout; /**< Cull non-open circuits that were born at * least this many seconds ago. Used until * adaptive algorithm learns a new value. */ @@ -3956,8 +4038,16 @@ typedef struct { int TokenBucketRefillInterval; char *AccelName; /**< Optional hardware acceleration engine name. */ char *AccelDir; /**< Optional hardware acceleration engine search dir. */ - int UseEntryGuards; /**< Boolean: Do we try to enter from a smallish number - * of fixed nodes? */ + + /** Boolean: Do we try to enter from a smallish number + * of fixed nodes? */ + int UseEntryGuards_option; + /** Internal variable to remember whether we're actually acting on + * UseEntryGuards_option -- when we're a non-anonymous Tor2web client or + * Single Onion Service, it is alwasy false, otherwise we use the value of + * UseEntryGuards_option. */ + int UseEntryGuards; + int NumEntryGuards; /**< How many entry guards do we try to establish? */ int UseEntryGuardsAsDirGuards; /** Boolean: Do we try to get directory info * from a smallish number of fixed nodes? */ @@ -4402,9 +4492,6 @@ typedef struct { char *TLSECGroup; /**< One of "P256", "P224", or nil for auto */ - /** Autobool: should we use the ntor handshake if we can? */ - int UseNTorHandshake; - /** Fraction: */ double PathsNeededToBuildCircuits; @@ -5073,7 +5160,8 @@ typedef struct rend_encoded_v2_service_descriptor_t { * the service side) and in rend_service_descriptor_t (on both the * client and service side). */ typedef struct rend_intro_point_t { - extend_info_t *extend_info; /**< Extend info of this introduction point. */ + extend_info_t *extend_info; /**< Extend info for connecting to this + * introduction point via a multi-hop path. */ crypto_pk_t *intro_key; /**< Introduction key that replaces the service * key, if this descriptor is V2. */ diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h index be7cba47d1..3a86c52f3c 100644 --- a/src/or/parsecommon.h +++ b/src/or/parsecommon.h @@ -35,6 +35,7 @@ typedef enum { K_RUNNING_ROUTERS, K_ROUTER_STATUS, K_PLATFORM, + K_PROTO, K_OPT, K_BANDWIDTH, K_CONTACT, @@ -51,6 +52,10 @@ typedef enum { K_DIR_OPTIONS, K_CLIENT_VERSIONS, K_SERVER_VERSIONS, + K_RECOMMENDED_CLIENT_PROTOCOLS, + K_RECOMMENDED_RELAY_PROTOCOLS, + K_REQUIRED_CLIENT_PROTOCOLS, + K_REQUIRED_RELAY_PROTOCOLS, K_OR_ADDRESS, K_ID, K_P, @@ -216,12 +221,14 @@ typedef enum { #define TS_NOCHECK 2 #define TS_NO_NEW_ANNOTATIONS 4 -/* +/** + * @name macros for defining token rules + * * Helper macros to define token tables. 's' is a string, 't' is a * directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an * object syntax. - * */ +/**@{*/ /** Appears to indicate the end of a table. */ #define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 } @@ -242,16 +249,17 @@ typedef enum { /** An annotation that must appear no more than once */ #define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 } -/* Argument multiplicity: any number of arguments. */ +/** Argument multiplicity: any number of arguments. */ #define ARGS 0,INT_MAX,0 -/* Argument multiplicity: no arguments. */ +/** Argument multiplicity: no arguments. */ #define NO_ARGS 0,0,0 -/* Argument multiplicity: concatenate all arguments. */ +/** Argument multiplicity: concatenate all arguments. */ #define CONCAT_ARGS 1,1,1 -/* Argument multiplicity: at least <b>n</b> arguments. */ +/** Argument multiplicity: at least <b>n</b> arguments. */ #define GE(n) n,INT_MAX,0 -/* Argument multiplicity: exactly <b>n</b> arguments. */ +/** Argument multiplicity: exactly <b>n</b> arguments. */ #define EQ(n) n,n,0 +/**@}*/ /** Determines the parsing rules for a single token type. */ typedef struct token_rule_t { diff --git a/src/or/periodic.c b/src/or/periodic.c index 0bccc6ec20..d02d4a7bbb 100644 --- a/src/or/periodic.c +++ b/src/or/periodic.c @@ -5,6 +5,10 @@ * \file periodic.c * * \brief Generic backend for handling periodic events. + * + * The events in this module are used by main.c to track items that need + * to fire once every N seconds, possibly picking a new interval each time + * that they fire. See periodic_events[] in main.c for examples. */ #include "or.h" diff --git a/src/or/policies.c b/src/or/policies.c index 07f256f5cc..9e4e73dfea 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -6,6 +6,13 @@ /** * \file policies.c * \brief Code to parse and use address policies and exit policies. + * + * We have two key kinds of address policy: full and compressed. A full + * policy is an array of accept/reject patterns, to be applied in order. + * A short policy is simply a list of ports. This module handles both + * kinds, including generic functions to apply them to addresses, and + * also including code to manage the global policies that we apply to + * incoming and outgoing connections. **/ #define POLICIES_PRIVATE @@ -2119,8 +2126,10 @@ exit_policy_is_general_exit_helper(smartlist_t *policy, int port) if (subnet_status[i] != 0) continue; /* We already reject some part of this /8 */ tor_addr_from_ipv4h(&addr, i<<24); - if (tor_addr_is_internal(&addr, 0)) + if (tor_addr_is_internal(&addr, 0) && + !get_options()->DirAllowPrivateAddresses) { continue; /* Local or non-routable addresses */ + } if (p->policy_type == ADDR_POLICY_ACCEPT) { if (p->maskbits > 8) continue; /* Narrower than a /8. */ @@ -2461,9 +2470,9 @@ policy_summarize(smartlist_t *policy, sa_family_t family) tor_snprintf(buf, sizeof(buf), "%d-%d", start_prt, AT(i)->prt_max); if (AT(i)->accepted) - smartlist_add(accepts, tor_strdup(buf)); + smartlist_add_strdup(accepts, buf); else - smartlist_add(rejects, tor_strdup(buf)); + smartlist_add_strdup(rejects, buf); if (last) break; @@ -2644,7 +2653,7 @@ write_short_policy(const short_policy_t *policy) smartlist_add_asprintf(sl, "%d-%d", e->min_port, e->max_port); } if (i < policy->n_entries-1) - smartlist_add(sl, tor_strdup(",")); + smartlist_add_strdup(sl, ","); } answer = smartlist_join_strings(sl, "", 0, NULL); SMARTLIST_FOREACH(sl, char *, a, tor_free(a)); diff --git a/src/or/protover.c b/src/or/protover.c new file mode 100644 index 0000000000..335be29e61 --- /dev/null +++ b/src/or/protover.c @@ -0,0 +1,736 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file protover.c + * \brief Versioning information for different pieces of the Tor protocol. + * + * Starting in version 0.2.9.3-alpha, Tor places separate version numbers on + * each of the different components of its protocol. Relays use these numbers + * to advertise what versions of the protocols they can support, and clients + * use them to find what they can ask a given relay to do. Authorities vote + * on the supported protocol versions for each relay, and also vote on the + * which protocols you should have to support in order to be on the Tor + * network. All Tor instances use these required/recommended protocol versions + * to + * + * The main advantage of these protocol versions numbers over using Tor + * version numbers is that they allow different implementations of the Tor + * protocols to develop independently, without having to claim compatibility + * with specific versions of Tor. + **/ + +#define PROTOVER_PRIVATE + +#include "or.h" +#include "protover.h" +#include "routerparse.h" + +static const smartlist_t *get_supported_protocol_list(void); +static int protocol_list_contains(const smartlist_t *protos, + protocol_type_t pr, uint32_t ver); + +/** Mapping between protocol type string and protocol type. */ +static const struct { + protocol_type_t protover_type; + const char *name; +} PROTOCOL_NAMES[] = { + { PRT_LINK, "Link" }, + { PRT_LINKAUTH, "LinkAuth" }, + { PRT_RELAY, "Relay" }, + { PRT_DIRCACHE, "DirCache" }, + { PRT_HSDIR, "HSDir" }, + { PRT_HSINTRO, "HSIntro" }, + { PRT_HSREND, "HSRend" }, + { PRT_DESC, "Desc" }, + { PRT_MICRODESC, "Microdesc"}, + { PRT_CONS, "Cons" } +}; + +#define N_PROTOCOL_NAMES ARRAY_LENGTH(PROTOCOL_NAMES) + +/** + * Given a protocol_type_t, return the corresponding string used in + * descriptors. + */ +STATIC const char * +protocol_type_to_str(protocol_type_t pr) +{ + unsigned i; + for (i=0; i < N_PROTOCOL_NAMES; ++i) { + if (PROTOCOL_NAMES[i].protover_type == pr) + return PROTOCOL_NAMES[i].name; + } + /* LCOV_EXCL_START */ + tor_assert_nonfatal_unreached_once(); + return "UNKNOWN"; + /* LCOV_EXCL_STOP */ +} + +/** + * Given a string, find the corresponding protocol type and store it in + * <b>pr_out</b>. Return 0 on success, -1 on failure. + */ +STATIC int +str_to_protocol_type(const char *s, protocol_type_t *pr_out) +{ + if (BUG(!pr_out)) + return -1; + + unsigned i; + for (i=0; i < N_PROTOCOL_NAMES; ++i) { + if (0 == strcmp(s, PROTOCOL_NAMES[i].name)) { + *pr_out = PROTOCOL_NAMES[i].protover_type; + return 0; + } + } + + return -1; +} + +/** + * Release all space held by a single proto_entry_t structure + */ +STATIC void +proto_entry_free(proto_entry_t *entry) +{ + if (!entry) + return; + tor_free(entry->name); + SMARTLIST_FOREACH(entry->ranges, proto_range_t *, r, tor_free(r)); + smartlist_free(entry->ranges); + tor_free(entry); +} + +/** + * Given a string <b>s</b> and optional end-of-string pointer + * <b>end_of_range</b>, parse the protocol range and store it in + * <b>low_out</b> and <b>high_out</b>. A protocol range has the format U, or + * U-U, where U is an unsigned 32-bit integer. + */ +static int +parse_version_range(const char *s, const char *end_of_range, + uint32_t *low_out, uint32_t *high_out) +{ + uint32_t low, high; + char *next = NULL; + int ok; + + tor_assert(high_out); + tor_assert(low_out); + + if (BUG(!end_of_range)) + end_of_range = s + strlen(s); // LCOV_EXCL_LINE + + /* Note that this wouldn't be safe if we didn't know that eventually, + * we'd hit a NUL */ + low = (uint32_t) tor_parse_ulong(s, 10, 0, UINT32_MAX, &ok, &next); + if (!ok) + goto error; + if (next > end_of_range) + goto error; + if (next == end_of_range) { + high = low; + goto done; + } + + if (*next != '-') + goto error; + s = next+1; + /* ibid */ + high = (uint32_t) tor_parse_ulong(s, 10, 0, UINT32_MAX, &ok, &next); + if (!ok) + goto error; + if (next != end_of_range) + goto error; + + done: + *high_out = high; + *low_out = low; + return 0; + + error: + return -1; +} + +/** Parse a single protocol entry from <b>s</b> up to an optional + * <b>end_of_entry</b> pointer, and return that protocol entry. Return NULL + * on error. + * + * A protocol entry has a keyword, an = sign, and zero or more ranges. */ +static proto_entry_t * +parse_single_entry(const char *s, const char *end_of_entry) +{ + proto_entry_t *out = tor_malloc_zero(sizeof(proto_entry_t)); + const char *equals; + + out->ranges = smartlist_new(); + + if (BUG (!end_of_entry)) + end_of_entry = s + strlen(s); // LCOV_EXCL_LINE + + /* There must be an =. */ + equals = memchr(s, '=', end_of_entry - s); + if (!equals) + goto error; + + /* The name must be nonempty */ + if (equals == s) + goto error; + + out->name = tor_strndup(s, equals-s); + + tor_assert(equals < end_of_entry); + + s = equals + 1; + while (s < end_of_entry) { + const char *comma = memchr(s, ',', end_of_entry-s); + proto_range_t *range = tor_malloc_zero(sizeof(proto_range_t)); + if (! comma) + comma = end_of_entry; + + smartlist_add(out->ranges, range); + if (parse_version_range(s, comma, &range->low, &range->high) < 0) { + goto error; + } + + if (range->low > range->high) { + goto error; + } + + s = comma; + while (*s == ',' && s < end_of_entry) + ++s; + } + + return out; + + error: + proto_entry_free(out); + return NULL; +} + +/** + * Parse the protocol list from <b>s</b> and return it as a smartlist of + * proto_entry_t + */ +STATIC smartlist_t * +parse_protocol_list(const char *s) +{ + smartlist_t *entries = smartlist_new(); + + while (*s) { + /* Find the next space or the NUL. */ + const char *end_of_entry = strchr(s, ' '); + proto_entry_t *entry; + if (!end_of_entry) + end_of_entry = s + strlen(s); + + entry = parse_single_entry(s, end_of_entry); + + if (! entry) + goto error; + + smartlist_add(entries, entry); + + s = end_of_entry; + while (*s == ' ') + ++s; + } + + return entries; + + error: + SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(entries); + return NULL; +} + +/** + * Given a protocol type and version number, return true iff we know + * how to speak that protocol. + */ +int +protover_is_supported_here(protocol_type_t pr, uint32_t ver) +{ + const smartlist_t *ours = get_supported_protocol_list(); + return protocol_list_contains(ours, pr, ver); +} + +/** + * Return true iff "list" encodes a protocol list that includes support for + * the indicated protocol and version. + */ +int +protocol_list_supports_protocol(const char *list, protocol_type_t tp, + uint32_t version) +{ + /* NOTE: This is a pretty inefficient implementation. If it ever shows + * up in profiles, we should memoize it. + */ + smartlist_t *protocols = parse_protocol_list(list); + if (!protocols) { + return 0; + } + int contains = protocol_list_contains(protocols, tp, version); + + SMARTLIST_FOREACH(protocols, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(protocols); + return contains; +} + +/** Return the canonical string containing the list of protocols + * that we support. */ +const char * +protover_get_supported_protocols(void) +{ + return + "Cons=1-2 " + "Desc=1-2 " + "DirCache=1 " + "HSDir=1 " + "HSIntro=3 " + "HSRend=1-2 " + "Link=1-4 " + "LinkAuth=1 " + "Microdesc=1-2 " + "Relay=1-2"; +} + +/** The protocols from protover_get_supported_protocols(), as parsed into a + * list of proto_entry_t values. Access this via + * get_supported_protocol_list. */ +static smartlist_t *supported_protocol_list = NULL; + +/** Return a pointer to a smartlist of proto_entry_t for the protocols + * we support. */ +static const smartlist_t * +get_supported_protocol_list(void) +{ + if (PREDICT_UNLIKELY(supported_protocol_list == NULL)) { + supported_protocol_list = + parse_protocol_list(protover_get_supported_protocols()); + } + return supported_protocol_list; +} + +/** + * Given a protocol entry, encode it at the end of the smartlist <b>chunks</b> + * as one or more newly allocated strings. + */ +static void +proto_entry_encode_into(smartlist_t *chunks, const proto_entry_t *entry) +{ + smartlist_add_asprintf(chunks, "%s=", entry->name); + + SMARTLIST_FOREACH_BEGIN(entry->ranges, proto_range_t *, range) { + const char *comma = ""; + if (range_sl_idx != 0) + comma = ","; + + if (range->low == range->high) { + smartlist_add_asprintf(chunks, "%s%lu", + comma, (unsigned long)range->low); + } else { + smartlist_add_asprintf(chunks, "%s%lu-%lu", + comma, (unsigned long)range->low, + (unsigned long)range->high); + } + } SMARTLIST_FOREACH_END(range); +} + +/** Given a list of space-separated proto_entry_t items, + * encode it into a newly allocated space-separated string. */ +STATIC char * +encode_protocol_list(const smartlist_t *sl) +{ + const char *separator = ""; + smartlist_t *chunks = smartlist_new(); + SMARTLIST_FOREACH_BEGIN(sl, const proto_entry_t *, ent) { + smartlist_add_strdup(chunks, separator); + + proto_entry_encode_into(chunks, ent); + + separator = " "; + } SMARTLIST_FOREACH_END(ent); + + char *result = smartlist_join_strings(chunks, "", 0, NULL); + + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); + + return result; +} + +/* We treat any protocol list with more than this many subprotocols in it + * as a DoS attempt. */ +static const int MAX_PROTOCOLS_TO_EXPAND = (1<<16); + +/** Voting helper: Given a list of proto_entry_t, return a newly allocated + * smartlist of newly allocated strings, one for each included protocol + * version. (So 'Foo=3,5-7' expands to a list of 'Foo=3', 'Foo=5', 'Foo=6', + * 'Foo=7'.) + * + * Do not list any protocol version more than once. + * + * Return NULL if the list would be too big. + */ +static smartlist_t * +expand_protocol_list(const smartlist_t *protos) +{ + smartlist_t *expanded = smartlist_new(); + if (!protos) + return expanded; + + SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) { + const char *name = ent->name; + SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) { + uint32_t u; + for (u = range->low; u <= range->high; ++u) { + smartlist_add_asprintf(expanded, "%s=%lu", name, (unsigned long)u); + if (smartlist_len(expanded) > MAX_PROTOCOLS_TO_EXPAND) + goto too_many; + } + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(ent); + + smartlist_sort_strings(expanded); + smartlist_uniq_strings(expanded); // This makes voting work. do not remove + return expanded; + + too_many: + SMARTLIST_FOREACH(expanded, char *, cp, tor_free(cp)); + smartlist_free(expanded); + return NULL; +} + +/** Voting helper: compare two singleton proto_entry_t items by version + * alone. (A singleton item is one with a single range entry where + * low==high.) */ +static int +cmp_single_ent_by_version(const void **a_, const void **b_) +{ + const proto_entry_t *ent_a = *a_; + const proto_entry_t *ent_b = *b_; + + tor_assert(smartlist_len(ent_a->ranges) == 1); + tor_assert(smartlist_len(ent_b->ranges) == 1); + + const proto_range_t *a = smartlist_get(ent_a->ranges, 0); + const proto_range_t *b = smartlist_get(ent_b->ranges, 0); + + tor_assert(a->low == a->high); + tor_assert(b->low == b->high); + + if (a->low < b->low) { + return -1; + } else if (a->low == b->low) { + return 0; + } else { + return 1; + } +} + +/** Voting helper: Given a list of singleton protocol strings (of the form + * Foo=7), return a canonical listing of all the protocol versions listed, + * with as few ranges as possible, with protocol versions sorted lexically and + * versions sorted in numerically increasing order, using as few range entries + * as possible. + **/ +static char * +contract_protocol_list(const smartlist_t *proto_strings) +{ + // map from name to list of single-version entries + strmap_t *entry_lists_by_name = strmap_new(); + // list of protocol names + smartlist_t *all_names = smartlist_new(); + // list of strings for the output we're building + smartlist_t *chunks = smartlist_new(); + + // Parse each item and stick it entry_lists_by_name. Build + // 'all_names' at the same time. + SMARTLIST_FOREACH_BEGIN(proto_strings, const char *, s) { + if (BUG(!s)) + continue;// LCOV_EXCL_LINE + proto_entry_t *ent = parse_single_entry(s, s+strlen(s)); + if (BUG(!ent)) + continue; // LCOV_EXCL_LINE + smartlist_t *lst = strmap_get(entry_lists_by_name, ent->name); + if (!lst) { + smartlist_add(all_names, ent->name); + lst = smartlist_new(); + strmap_set(entry_lists_by_name, ent->name, lst); + } + smartlist_add(lst, ent); + } SMARTLIST_FOREACH_END(s); + + // We want to output the protocols sorted by their name. + smartlist_sort_strings(all_names); + + SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) { + const int first_entry = (name_sl_idx == 0); + smartlist_t *lst = strmap_get(entry_lists_by_name, name); + tor_assert(lst); + // Sort every entry with this name by version. They are + // singletons, so there can't be overlap. + smartlist_sort(lst, cmp_single_ent_by_version); + + if (! first_entry) + smartlist_add_strdup(chunks, " "); + + /* We're going to construct this entry from the ranges. */ + proto_entry_t *entry = tor_malloc_zero(sizeof(proto_entry_t)); + entry->ranges = smartlist_new(); + entry->name = tor_strdup(name); + + // Now, find all the ranges of versions start..end where + // all of start, start+1, start+2, ..end are included. + int start_of_cur_series = 0; + while (start_of_cur_series < smartlist_len(lst)) { + const proto_entry_t *ent = smartlist_get(lst, start_of_cur_series); + const proto_range_t *range = smartlist_get(ent->ranges, 0); + const uint32_t ver_low = range->low; + uint32_t ver_high = ver_low; + + int idx; + for (idx = start_of_cur_series+1; idx < smartlist_len(lst); ++idx) { + ent = smartlist_get(lst, idx); + range = smartlist_get(ent->ranges, 0); + if (range->low != ver_high + 1) + break; + ver_high += 1; + } + + // Now idx is either off the end of the list, or the first sequence + // break in the list. + start_of_cur_series = idx; + + proto_range_t *new_range = tor_malloc_zero(sizeof(proto_range_t)); + new_range->low = ver_low; + new_range->high = ver_high; + smartlist_add(entry->ranges, new_range); + } + proto_entry_encode_into(chunks, entry); + proto_entry_free(entry); + + } SMARTLIST_FOREACH_END(name); + + // Build the result... + char *result = smartlist_join_strings(chunks, "", 0, NULL); + + // And free all the stuff we allocated. + SMARTLIST_FOREACH_BEGIN(all_names, const char *, name) { + smartlist_t *lst = strmap_get(entry_lists_by_name, name); + tor_assert(lst); + SMARTLIST_FOREACH(lst, proto_entry_t *, e, proto_entry_free(e)); + smartlist_free(lst); + } SMARTLIST_FOREACH_END(name); + + strmap_free(entry_lists_by_name, NULL); + smartlist_free(all_names); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); + + return result; +} + +/** + * Protocol voting implementation. + * + * Given a list of strings describing protocol versions, return a newly + * allocated string encoding all of the protocols that are listed by at + * least <b>threshold</b> of the inputs. + * + * The string is minimal and sorted according to the rules of + * contract_protocol_list above. + */ +char * +protover_compute_vote(const smartlist_t *list_of_proto_strings, + int threshold) +{ + smartlist_t *all_entries = smartlist_new(); + + // First, parse the inputs and break them into singleton entries. + SMARTLIST_FOREACH_BEGIN(list_of_proto_strings, const char *, vote) { + smartlist_t *unexpanded = parse_protocol_list(vote); + smartlist_t *this_vote = expand_protocol_list(unexpanded); + if (this_vote == NULL) { + log_warn(LD_NET, "When expanding a protocol list from an authority, I " + "got too many protocols. This is possibly an attack or a bug, " + "unless the Tor network truly has expanded to support over %d " + "different subprotocol versions. The offending string was: %s", + MAX_PROTOCOLS_TO_EXPAND, escaped(vote)); + } else { + smartlist_add_all(all_entries, this_vote); + smartlist_free(this_vote); + } + SMARTLIST_FOREACH(unexpanded, proto_entry_t *, e, proto_entry_free(e)); + smartlist_free(unexpanded); + } SMARTLIST_FOREACH_END(vote); + + // Now sort the singleton entries + smartlist_sort_strings(all_entries); + + // Now find all the strings that appear at least 'threshold' times. + smartlist_t *include_entries = smartlist_new(); + const char *cur_entry = smartlist_get(all_entries, 0); + int n_times = 0; + SMARTLIST_FOREACH_BEGIN(all_entries, const char *, ent) { + if (!strcmp(ent, cur_entry)) { + n_times++; + } else { + if (n_times >= threshold && cur_entry) + smartlist_add(include_entries, (void*)cur_entry); + cur_entry = ent; + n_times = 1 ; + } + } SMARTLIST_FOREACH_END(ent); + + if (n_times >= threshold && cur_entry) + smartlist_add(include_entries, (void*)cur_entry); + + // Finally, compress that list. + char *result = contract_protocol_list(include_entries); + smartlist_free(include_entries); + SMARTLIST_FOREACH(all_entries, char *, cp, tor_free(cp)); + smartlist_free(all_entries); + + return result; +} + +/** Return true if every protocol version described in the string <b>s</b> is + * one that we support, and false otherwise. If <b>missing_out</b> is + * provided, set it to the list of protocols we do not support. + * + * NOTE: This is quadratic, but we don't do it much: only a few times per + * consensus. Checking signatures should be way more expensive than this + * ever would be. + **/ +int +protover_all_supported(const char *s, char **missing_out) +{ + int all_supported = 1; + smartlist_t *missing; + + if (!s) { + return 1; + } + + smartlist_t *entries = parse_protocol_list(s); + + missing = smartlist_new(); + + SMARTLIST_FOREACH_BEGIN(entries, const proto_entry_t *, ent) { + protocol_type_t tp; + if (str_to_protocol_type(ent->name, &tp) < 0) { + if (smartlist_len(ent->ranges)) { + goto unsupported; + } + continue; + } + + SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) { + uint32_t i; + for (i = range->low; i <= range->high; ++i) { + if (!protover_is_supported_here(tp, i)) { + goto unsupported; + } + } + } SMARTLIST_FOREACH_END(range); + + continue; + + unsupported: + all_supported = 0; + smartlist_add(missing, (void*) ent); + } SMARTLIST_FOREACH_END(ent); + + if (missing_out && !all_supported) { + tor_assert(0 != smartlist_len(missing)); + *missing_out = encode_protocol_list(missing); + } + smartlist_free(missing); + + SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(entries); + + return all_supported; +} + +/** Helper: Given a list of proto_entry_t, return true iff + * <b>pr</b>=<b>ver</b> is included in that list. */ +static int +protocol_list_contains(const smartlist_t *protos, + protocol_type_t pr, uint32_t ver) +{ + if (BUG(protos == NULL)) { + return 0; // LCOV_EXCL_LINE + } + const char *pr_name = protocol_type_to_str(pr); + if (BUG(pr_name == NULL)) { + return 0; // LCOV_EXCL_LINE + } + + SMARTLIST_FOREACH_BEGIN(protos, const proto_entry_t *, ent) { + if (strcasecmp(ent->name, pr_name)) + continue; + /* name matches; check the ranges */ + SMARTLIST_FOREACH_BEGIN(ent->ranges, const proto_range_t *, range) { + if (ver >= range->low && ver <= range->high) + return 1; + } SMARTLIST_FOREACH_END(range); + } SMARTLIST_FOREACH_END(ent); + + return 0; +} + +/** Return a string describing the protocols supported by tor version + * <b>version</b>, or an empty string if we cannot tell. + * + * Note that this is only used to infer protocols for Tor versions that + * can't declare their own. + **/ +const char * +protover_compute_for_old_tor(const char *version) +{ + if (tor_version_as_new_as(version, + FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS)) { + return ""; + } else if (tor_version_as_new_as(version, "0.2.7.5")) { + /* 0.2.9.1-alpha HSRend=2 */ + return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 " + "Link=1-4 LinkAuth=1 " + "Microdesc=1-2 Relay=1-2"; + } else if (tor_version_as_new_as(version, "0.2.7.5")) { + /* 0.2.7-stable added Desc=2, Microdesc=2, Cons=2, which indicate + * ed25519 support. We'll call them present only in "stable" 027, + * though. */ + return "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=1-4 LinkAuth=1 " + "Microdesc=1-2 Relay=1-2"; + } else if (tor_version_as_new_as(version, "0.2.4.19")) { + /* No currently supported Tor server versions are older than this, or + * lack these protocols. */ + return "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " + "Link=1-4 LinkAuth=1 " + "Microdesc=1 Relay=1-2"; + } else { + /* Cannot infer protocols. */ + return ""; + } +} + +/** + * Release all storage held by static fields in protover.c + */ +void +protover_free_all(void) +{ + if (supported_protocol_list) { + smartlist_t *entries = supported_protocol_list; + SMARTLIST_FOREACH(entries, proto_entry_t *, ent, proto_entry_free(ent)); + smartlist_free(entries); + supported_protocol_list = NULL; + } +} + diff --git a/src/or/protover.h b/src/or/protover.h new file mode 100644 index 0000000000..5c658931ea --- /dev/null +++ b/src/or/protover.h @@ -0,0 +1,74 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file protover.h + * \brief Headers and type declarations for protover.c + **/ + +#ifndef TOR_PROTOVER_H +#define TOR_PROTOVER_H + +#include "container.h" + +/** The first version of Tor that included "proto" entries in its + * descriptors. Authorities should use this to decide whether to + * guess proto lines. */ +/* This is a guess. */ +#define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" + +/** List of recognized subprotocols. */ +typedef enum protocol_type_t { + PRT_LINK, + PRT_LINKAUTH, + PRT_RELAY, + PRT_DIRCACHE, + PRT_HSDIR, + PRT_HSINTRO, + PRT_HSREND, + PRT_DESC, + PRT_MICRODESC, + PRT_CONS, +} protocol_type_t; + +int protover_all_supported(const char *s, char **missing); +int protover_is_supported_here(protocol_type_t pr, uint32_t ver); +const char *protover_get_supported_protocols(void); + +char *protover_compute_vote(const smartlist_t *list_of_proto_strings, + int threshold); +const char *protover_compute_for_old_tor(const char *version); +int protocol_list_supports_protocol(const char *list, protocol_type_t tp, + uint32_t version); + +void protover_free_all(void); + +#ifdef PROTOVER_PRIVATE +/** Represents a range of subprotocols of a given type. All subprotocols + * between <b>low</b> and <b>high</b> inclusive are included. */ +typedef struct proto_range_t { + uint32_t low; + uint32_t high; +} proto_range_t; + +/** Represents a set of ranges of subprotocols of a given type. */ +typedef struct proto_entry_t { + /** The name of the protocol. + * + * (This needs to handle voting on protocols which + * we don't recognize yet, so it's a char* rather than a protocol_type_t.) + */ + char *name; + /** Smartlist of proto_range_t */ + smartlist_t *ranges; +} proto_entry_t; + +STATIC smartlist_t *parse_protocol_list(const char *s); +STATIC void proto_entry_free(proto_entry_t *entry); +STATIC char *encode_protocol_list(const smartlist_t *sl); +STATIC const char *protocol_type_to_str(protocol_type_t pr); +STATIC int str_to_protocol_type(const char *s, protocol_type_t *pr_out); +#endif + +#endif + diff --git a/src/or/reasons.c b/src/or/reasons.c index 36921cafcd..a1566e2299 100644 --- a/src/or/reasons.c +++ b/src/or/reasons.c @@ -6,6 +6,12 @@ * \file reasons.c * \brief Convert circuit, stream, and orconn error reasons to and/or from * strings and errno values. + * + * This module is just a bunch of functions full of case statements that + * convert from one representation of our error codes to another. These are + * mainly used in generating log messages, in sending messages to the + * controller in control.c, and in converting errors from one protocol layer + * to another. **/ #include "or.h" diff --git a/src/or/relay.c b/src/or/relay.c index e2963e3288..8d48239e47 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -8,6 +8,41 @@ * \file relay.c * \brief Handle relay cell encryption/decryption, plus packaging and * receiving from circuits, plus queuing on circuits. + * + * This is a core modules that makes Tor work. It's responsible for + * dealing with RELAY cells (the ones that travel more than one hop along a + * circuit), by: + * <ul> + * <li>constructing relays cells, + * <li>encrypting relay cells, + * <li>decrypting relay cells, + * <li>demultiplexing relay cells as they arrive on a connection, + * <li>queueing relay cells for retransmission, + * <li>or handling relay cells that are for us to receive (as an exit or a + * client). + * </ul> + * + * RELAY cells are generated throughout the code at the client or relay side, + * using relay_send_command_from_edge() or one of the functions like + * connection_edge_send_command() that calls it. Of particular interest is + * connection_edge_package_raw_inbuf(), which takes information that has + * arrived on an edge connection socket, and packages it as a RELAY_DATA cell + * -- this is how information is actually sent across the Tor network. The + * cryptography for these functions is handled deep in + * circuit_package_relay_cell(), which either adds a single layer of + * encryption (if we're an exit), or multiple layers (if we're the origin of + * the circuit). After construction and encryption, the RELAY cells are + * passed to append_cell_to_circuit_queue(), which queues them for + * transmission and tells the circuitmux (see circuitmux.c) that the circuit + * is waiting to send something. + * + * Incoming RELAY cells arrive at circuit_receive_relay_cell(), called from + * command.c. There they are decrypted and, if they are for us, are passed to + * connection_edge_process_relay_cell(). If they're not for us, they're + * re-queued for retransmission again with append_cell_to_circuit_queue(). + * + * The connection_edge_process_relay_cell() function handles all the different + * types of relay cells, launching requests or transmitting data as needed. **/ #define RELAY_PRIVATE @@ -576,14 +611,14 @@ relay_send_command_from_edge_(streamid_t stream_id, circuit_t *circ, memset(&cell, 0, sizeof(cell_t)); cell.command = CELL_RELAY; - if (cpath_layer) { + if (CIRCUIT_IS_ORIGIN(circ)) { + tor_assert(cpath_layer); cell.circ_id = circ->n_circ_id; cell_direction = CELL_DIRECTION_OUT; - } else if (! CIRCUIT_IS_ORIGIN(circ)) { + } else { + tor_assert(! cpath_layer); cell.circ_id = TO_OR_CIRCUIT(circ)->p_circ_id; cell_direction = CELL_DIRECTION_IN; - } else { - return -1; } memset(&rh, 0, sizeof(rh)); @@ -2453,7 +2488,7 @@ update_circuit_on_cmux_(circuit_t *circ, cell_direction_t direction, /* Cmux sanity check */ if (! circuitmux_is_circuit_attached(cmux, circ)) { - log_warn(LD_BUG, "called on non-attachd circuit from %s:%d", + log_warn(LD_BUG, "called on non-attached circuit from %s:%d", file, lineno); return; } @@ -2612,6 +2647,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/rendclient.c b/src/or/rendclient.c index 0bfc1a1805..b0dcf52507 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -135,6 +135,7 @@ int rend_client_send_introduction(origin_circuit_t *introcirc, origin_circuit_t *rendcirc) { + const or_options_t *options = get_options(); size_t payload_len; int r, v3_shift = 0; char payload[RELAY_PAYLOAD_SIZE]; @@ -152,10 +153,8 @@ rend_client_send_introduction(origin_circuit_t *introcirc, tor_assert(rendcirc->rend_data); tor_assert(!rend_cmp_service_ids(rend_data_get_address(introcirc->rend_data), rend_data_get_address(rendcirc->rend_data))); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(introcirc->build_state->onehop_tunnel)); - tor_assert(!(rendcirc->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(introcirc, options); + assert_circ_anonymity_ok(rendcirc, options); onion_address = rend_data_get_address(introcirc->rend_data); r = rend_cache_lookup_entry(onion_address, -1, &entry); @@ -388,6 +387,7 @@ int rend_client_introduction_acked(origin_circuit_t *circ, const uint8_t *request, size_t request_len) { + const or_options_t *options = get_options(); origin_circuit_t *rendcirc; (void) request; // XXXX Use this. @@ -399,10 +399,9 @@ rend_client_introduction_acked(origin_circuit_t *circ, return -1; } + tor_assert(circ->build_state); tor_assert(circ->build_state->chosen_exit); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circ->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circ, options); tor_assert(circ->rend_data); /* For path bias: This circuit was used successfully. Valid @@ -417,9 +416,7 @@ rend_client_introduction_acked(origin_circuit_t *circ, log_info(LD_REND,"Received ack. Telling rend circ..."); rendcirc = circuit_get_ready_rend_circ_by_rend_data(circ->rend_data); if (rendcirc) { /* remember the ack */ -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(rendcirc->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(rendcirc, options); circuit_change_purpose(TO_CIRCUIT(rendcirc), CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); /* Set timestamp_dirty, because circuit_expire_building expects @@ -1386,40 +1383,20 @@ rend_client_get_random_intro_impl(const rend_cache_entry_t *entry, i = crypto_rand_int(smartlist_len(usable_nodes)); intro = smartlist_get(usable_nodes, i); - /* Do we need to look up the router or is the extend info complete? */ - if (!intro->extend_info->onion_key) { - const node_t *node; - extend_info_t *new_extend_info; - if (tor_digest_is_zero(intro->extend_info->identity_digest)) - node = node_get_by_hex_id(intro->extend_info->nickname); - else - node = node_get_by_id(intro->extend_info->identity_digest); - if (!node) { - log_info(LD_REND, "Unknown router with nickname '%s'; trying another.", - intro->extend_info->nickname); - 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'%s; trying another.", - extend_info_describe(intro->extend_info), alternate_reason); - smartlist_del(usable_nodes, i); - goto again; - } else { - extend_info_free(intro->extend_info); - intro->extend_info = new_extend_info; - } - tor_assert(intro->extend_info != NULL); + if (BUG(!intro->extend_info)) { + /* This should never happen, but it isn't fatal, just try another */ + smartlist_del(usable_nodes, i); + goto again; + } + /* All version 2 HS descriptors come with a TAP onion key. + * Clients used to try to get the TAP onion key from the consensus, but this + * meant that hidden services could discover which consensus clients have. */ + if (!extend_info_supports_tap(intro->extend_info)) { + log_info(LD_REND, "The HS descriptor is missing a TAP onion key for the " + "intro-point relay '%s'; trying another.", + safe_str_client(extend_info_describe(intro->extend_info))); + smartlist_del(usable_nodes, i); + goto again; } /* Check if we should refuse to talk to this router. */ if (strict && @@ -1565,3 +1542,35 @@ rend_parse_service_authorization(const or_options_t *options, return res; } +/* Can Tor client code make direct (non-anonymous) connections to introduction + * or rendezvous points? + * Returns true if tor was compiled with NON_ANONYMOUS_MODE_ENABLED, and is + * configured in Tor2web mode. */ +int +rend_client_allow_non_anonymous_connection(const or_options_t *options) +{ + /* Tor2web support needs to be compiled in to a tor binary. */ +#ifdef NON_ANONYMOUS_MODE_ENABLED + /* Tor2web */ + return options->Tor2webMode ? 1 : 0; +#else + (void)options; + return 0; +#endif +} + +/* At compile-time, was non-anonymous mode enabled via + * NON_ANONYMOUS_MODE_ENABLED ? */ +int +rend_client_non_anonymous_mode_enabled(const or_options_t *options) +{ + (void)options; + /* Tor2web support needs to be compiled in to a tor binary. */ +#ifdef NON_ANONYMOUS_MODE_ENABLED + /* Tor2web */ + return 1; +#else + return 0; +#endif +} + diff --git a/src/or/rendclient.h b/src/or/rendclient.h index 459988d349..164305a773 100644 --- a/src/or/rendclient.h +++ b/src/or/rendclient.h @@ -51,5 +51,8 @@ rend_service_authorization_t *rend_client_lookup_service_authorization( const char *onion_address); void rend_service_authorization_free_all(void); +int rend_client_allow_non_anonymous_connection(const or_options_t *options); +int rend_client_non_anonymous_mode_enabled(const or_options_t *options); + #endif diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 125aa0f5ef..1e5d1ab121 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -950,6 +950,55 @@ rend_auth_decode_cookie(const char *cookie_in, uint8_t *cookie_out, return res; } +/* Is this a rend client or server that allows direct (non-anonymous) + * connections? + * Clients must be specifically compiled and configured in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_allow_non_anonymous_connection() or + * rend_service_allow_non_anonymous_connection() whenever possible, so that + * checks are specific to Single Onion Services or Tor2web. */ +int +rend_allow_non_anonymous_connection(const or_options_t* options) +{ + return (rend_client_allow_non_anonymous_connection(options) + || rend_service_allow_non_anonymous_connection(options)); +} + +/* Is this a rend client or server in non-anonymous mode? + * Clients must be specifically compiled in this mode. + * Onion services can be configured to start in this mode. + * Prefer rend_client_non_anonymous_mode_enabled() or + * rend_service_non_anonymous_mode_enabled() whenever possible, so that checks + * are specific to Single Onion Services or Tor2web. */ +int +rend_non_anonymous_mode_enabled(const or_options_t *options) +{ + return (rend_client_non_anonymous_mode_enabled(options) + || rend_service_non_anonymous_mode_enabled(options)); +} + +/* Make sure that tor only builds one-hop circuits when they would not + * compromise user anonymity. + * + * One-hop circuits are permitted in Tor2web or Single Onion modes. + * + * Tor2web or Single Onion modes are also allowed to make multi-hop circuits. + * For example, single onion HSDir circuits are 3-hop to prevent denial of + * service. + */ +void +assert_circ_anonymity_ok(origin_circuit_t *circ, + const or_options_t *options) +{ + tor_assert(options); + tor_assert(circ); + tor_assert(circ->build_state); + + if (circ->build_state->onehop_tunnel) { + tor_assert(rend_allow_non_anonymous_connection(options)); + } +} + /* Return 1 iff the given <b>digest</b> of a permenanent hidden service key is * equal to the digest in the origin circuit <b>ocirc</b> of its rend data . * If the rend data doesn't exist, 0 is returned. This function is agnostic to @@ -979,3 +1028,4 @@ rend_circuit_pk_digest_eq(const origin_circuit_t *ocirc, return 1; } + diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h index 1a893e1663..942ace5761 100644 --- a/src/or/rendcommon.h +++ b/src/or/rendcommon.h @@ -57,5 +57,11 @@ int rend_auth_decode_cookie(const char *cookie_in, rend_auth_type_t *auth_type_out, char **err_msg_out); +int rend_allow_non_anonymous_connection(const or_options_t* options); +int rend_non_anonymous_mode_enabled(const or_options_t *options); + +void assert_circ_anonymity_ok(origin_circuit_t *circ, + const or_options_t *options); + #endif diff --git a/src/or/rendmid.c b/src/or/rendmid.c index ca0ad7b0d4..f39c92afae 100644 --- a/src/or/rendmid.c +++ b/src/or/rendmid.c @@ -106,7 +106,7 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, RELAY_COMMAND_INTRO_ESTABLISHED, "", 0, NULL)<0) { log_info(LD_GENERAL, "Couldn't send INTRO_ESTABLISHED cell."); - goto err; + goto err_no_close; } /* Now, set up this circuit. */ @@ -122,8 +122,9 @@ rend_mid_establish_intro(or_circuit_t *circ, const uint8_t *request, log_warn(LD_PROTOCOL, "Rejecting truncated ESTABLISH_INTRO cell."); reason = END_CIRC_REASON_TORPROTOCOL; err: - if (pk) crypto_pk_free(pk); circuit_mark_for_close(TO_CIRCUIT(circ), reason); + err_no_close: + if (pk) crypto_pk_free(pk); return -1; } @@ -201,14 +202,15 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, (char*)request, request_len, NULL)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE2 cell to Tor client."); - goto err; + /* Stop right now, the circuit has been closed. */ + return -1; } /* 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)) { log_warn(LD_GENERAL, "Unable to send INTRODUCE_ACK cell to Tor client."); - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); + /* Stop right now, the circuit has been closed. */ return -1; } @@ -220,8 +222,6 @@ rend_mid_introduce(or_circuit_t *circ, const uint8_t *request, RELAY_COMMAND_INTRODUCE_ACK, nak_body, 1, NULL)) { log_warn(LD_GENERAL, "Unable to send NAK to Tor client."); - /* Is this right? */ - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); } return -1; } @@ -269,8 +269,8 @@ rend_mid_establish_rendezvous(or_circuit_t *circ, const uint8_t *request, RELAY_COMMAND_RENDEZVOUS_ESTABLISHED, "", 0, NULL)<0) { log_warn(LD_PROTOCOL, "Couldn't send RENDEZVOUS_ESTABLISHED cell."); - reason = END_CIRC_REASON_INTERNAL; - goto err; + /* Stop right now, the circuit has been closed. */ + return -1; } circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_REND_POINT_WAITING); @@ -346,7 +346,8 @@ rend_mid_rendezvous(or_circuit_t *circ, const uint8_t *request, log_warn(LD_GENERAL, "Unable to send RENDEZVOUS2 cell to client on circuit %u.", (unsigned)rend_circ->p_circ_id); - goto err; + /* Stop right now, the circuit has been closed. */ + return -1; } /* Join the circuits. */ diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 21e88eb9e8..b6bf63d829 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -21,6 +21,7 @@ #include "main.h" #include "networkstatus.h" #include "nodelist.h" +#include "policies.h" #include "rendclient.h" #include "rendcommon.h" #include "rendservice.h" @@ -108,66 +109,28 @@ struct rend_service_port_config_s { * rendezvous point before giving up? */ #define MAX_REND_TIMEOUT 30 -/** 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. 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 */ - rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client - * authorization is performed. */ - smartlist_t *clients; /**< List of rend_authorized_client_t's of - * clients that may access our service. Can be NULL - * if no client authorization is performed. */ - /* Other fields */ - crypto_pk_t *private_key; /**< Permanent hidden-service key. */ - char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without - * '.onion' */ - 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 - * established in this period. */ - unsigned int n_intro_points_wanted; /**< Number of intro points this - * service wants to have open. */ - rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ - time_t desc_is_dirty; /**< Time at which changes to the hidden service - * descriptor content occurred, or 0 if it's - * up-to-date. */ - time_t next_upload_time; /**< Scheduled next hidden service descriptor - * upload time. */ - /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to - * detect repeats. Clients may send INTRODUCE1 cells for the same - * rendezvous point through two or more different introduction points; - * when they do, this keeps us from launching multiple simultaneous attempts - * to connect to the same rend point. */ - replaycache_t *accepted_intro_dh_parts; - /** 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; +/* Hidden service directory file names: + * new file names should be added to rend_service_add_filenames_to_list() + * for sandboxing purposes. */ +static const char *private_key_fname = "private_key"; +static const char *hostname_fname = "hostname"; +static const char *client_keys_fname = "client_keys"; +static const char *sos_poison_fname = "onion_service_non_anonymous"; + +/** Tells if onion service <b>s</b> is ephemeral. +*/ +static unsigned int +rend_service_is_ephemeral(const struct rend_service_t *s) +{ + return (s->directory == NULL); +} /** 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]"; + return rend_service_is_ephemeral(s) ? "[EPHEMERAL]" : escaped(s->directory); } /** A list of rend_service_t's for services run on this OP. @@ -207,16 +170,18 @@ rend_authorized_client_strmap_item_free(void *authorized_client) /** Release the storage held by <b>service</b>. */ -static void +STATIC void rend_service_free(rend_service_t *service) { if (!service) return; tor_free(service->directory); - SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p, - rend_service_port_config_free(p)); - smartlist_free(service->ports); + if (service->ports) { + 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); if (service->intro_nodes) { @@ -317,7 +282,8 @@ 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. */ - if (service->directory != NULL) { /* Skip dupe for ephemeral services. */ + if (!rend_service_is_ephemeral(service)) { + /* Skip dupe for ephemeral services. */ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, dupe = dupe || !strcmp(ptr->directory, service->directory)); @@ -330,8 +296,8 @@ rend_add_service(rend_service_t *service) } } smartlist_add(rend_service_list, service); - log_debug(LD_REND,"Configuring service with directory \"%s\"", - service->directory); + log_debug(LD_REND,"Configuring service with directory %s", + rend_service_escaped_dir(service)); for (i = 0; i < smartlist_len(service->ports); ++i) { p = smartlist_get(service->ports, i); if (!(p->is_unix_addr)) { @@ -391,22 +357,20 @@ rend_service_parse_port_config(const char *string, const char *sep, int realport = 0; uint16_t p; tor_addr_t addr; - const char *addrport; rend_service_port_config_t *result = NULL; unsigned int is_unix_addr = 0; - char *socket_path = NULL; + const char *socket_path = NULL; char *err_msg = NULL; + char *addrport = NULL; sl = smartlist_new(); smartlist_split_string(sl, string, sep, - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) { + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 2); + if (smartlist_len(sl) < 1 || BUG(smartlist_len(sl) > 2)) { 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) { if (err_msg_out) @@ -415,7 +379,6 @@ rend_service_parse_port_config(const char *string, const char *sep, goto err; } - if (smartlist_len(sl) == 1) { /* No addr:port part; use default. */ realport = virtport; @@ -423,17 +386,18 @@ rend_service_parse_port_config(const char *string, const char *sep, } else { int ret; - addrport = smartlist_get(sl,1); - ret = config_parse_unix_port(addrport, &socket_path); - if (ret < 0 && ret != -ENOENT) { - if (ret == -EINVAL) - if (err_msg_out) - err_msg = tor_strdup("Empty socket path in hidden service port " - "configuration."); - + const char *addrport_element = smartlist_get(sl,1); + const char *rest = NULL; + int is_unix; + ret = port_cfg_line_extract_addrport(addrport_element, &addrport, + &is_unix, &rest); + if (ret < 0) { + tor_asprintf(&err_msg, "Couldn't process address <%s> from hidden " + "service configuration", addrport_element); goto err; } - if (socket_path) { + if (is_unix) { + socket_path = addrport; is_unix_addr = 1; } else if (strchr(addrport, ':') || strchr(addrport, '.')) { /* else try it as an IP:port pair if it has a : or . in it */ @@ -471,10 +435,10 @@ rend_service_parse_port_config(const char *string, const char *sep, } err: + tor_free(addrport); 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); return result; } @@ -509,7 +473,7 @@ rend_config_services(const or_options_t *options, int validate_only) if (!strcasecmp(line->key, "HiddenServiceDir")) { if (service) { /* register the one we just finished parsing */ if (validate_only) - rend_service_free(service); + rend_service_free(service); else rend_add_service(service); } @@ -550,7 +514,8 @@ rend_config_services(const or_options_t *options, int validate_only) } log_info(LD_CONFIG, "HiddenServiceAllowUnknownPorts=%d for %s", - (int)service->allow_unknown_ports, service->directory); + (int)service->allow_unknown_ports, + rend_service_escaped_dir(service)); } else if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) { service->dir_group_readable = (int)tor_parse_long(line->value, @@ -564,7 +529,8 @@ rend_config_services(const or_options_t *options, int validate_only) } log_info(LD_CONFIG, "HiddenServiceDirGroupReadable=%d for %s", - service->dir_group_readable, service->directory); + service->dir_group_readable, + rend_service_escaped_dir(service)); } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) { service->max_streams_per_circuit = (int)tor_parse_long(line->value, 10, 0, 65535, &ok, NULL); @@ -577,7 +543,8 @@ rend_config_services(const or_options_t *options, int validate_only) } log_info(LD_CONFIG, "HiddenServiceMaxStreams=%d for %s", - service->max_streams_per_circuit, service->directory); + service->max_streams_per_circuit, + rend_service_escaped_dir(service)); } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) { service->max_streams_close_circuit = (int)tor_parse_long(line->value, 10, 0, 1, &ok, NULL); @@ -591,7 +558,8 @@ rend_config_services(const or_options_t *options, int validate_only) } log_info(LD_CONFIG, "HiddenServiceMaxStreamsCloseCircuit=%d for %s", - (int)service->max_streams_close_circuit, service->directory); + (int)service->max_streams_close_circuit, + rend_service_escaped_dir(service)); } else if (!strcasecmp(line->key, "HiddenServiceNumIntroductionPoints")) { service->n_intro_points_wanted = (unsigned int) tor_parse_long(line->value, 10, @@ -607,7 +575,8 @@ rend_config_services(const or_options_t *options, int validate_only) return -1; } log_info(LD_CONFIG, "HiddenServiceNumIntroductionPoints=%d for %s", - service->n_intro_points_wanted, service->directory); + service->n_intro_points_wanted, + rend_service_escaped_dir(service)); } 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 @@ -913,7 +882,7 @@ rend_service_del_ephemeral(const char *service_id) "removal."); return -1; } - if (s->directory) { + if (!rend_service_is_ephemeral(s)) { log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal."); return -1; } @@ -1002,17 +971,239 @@ rend_service_update_descriptor(rend_service_t *service) } } +/* Allocate and return a string containing the path to file_name in + * service->directory. Asserts that service has a directory. + * This function will never return NULL. + * The caller must free this path. */ +static char * +rend_service_path(const rend_service_t *service, const char *file_name) +{ + char *file_path = NULL; + + tor_assert(service->directory); + + /* Can never fail: asserts rather than leaving file_path NULL. */ + tor_asprintf(&file_path, "%s%s%s", + service->directory, PATH_SEPARATOR, file_name); + + return file_path; +} + +/* Allocate and return a string containing the path to the single onion + * service poison file in service->directory. Asserts that service has a + * directory. + * The caller must free this path. */ +STATIC char * +rend_service_sos_poison_path(const rend_service_t *service) +{ + return rend_service_path(service, sos_poison_fname); +} + +/** Return True if hidden services <b>service</b> has been poisoned by single + * onion mode. */ +static int +service_is_single_onion_poisoned(const rend_service_t *service) +{ + char *poison_fname = NULL; + file_status_t fstatus; + + if (rend_service_is_ephemeral(service)) { + return 0; + } + + poison_fname = rend_service_sos_poison_path(service); + + fstatus = file_status(poison_fname); + tor_free(poison_fname); + + /* If this fname is occupied, the hidden service has been poisoned. */ + if (fstatus == FN_FILE || fstatus == FN_EMPTY) { + return 1; + } + + return 0; +} + +/* Return 1 if the private key file for service exists and has a non-zero size, + * and 0 otherwise. */ +static int +rend_service_private_key_exists(const rend_service_t *service) +{ + char *private_key_path = rend_service_path(service, private_key_fname); + const file_status_t private_key_status = file_status(private_key_path); + tor_free(private_key_path); + /* Only non-empty regular private key files could have been used before. */ + return private_key_status == FN_FILE; +} + +/** Check the single onion service poison state of all existing hidden service + * directories: + * - If each service is poisoned, and we are in Single Onion Mode, + * return 0, + * - If each service is not poisoned, and we are not in Single Onion Mode, + * return 0, + * - Otherwise, the poison state is invalid, and a service that was created in + * one mode is being used in the other, return -1. + * Hidden service directories without keys are not checked for consistency. + * When their keys are created, they will be poisoned (if needed). + * If a <b>service_list</b> is provided, treat it + * as the list of hidden services (used in unittests). */ +int +rend_service_list_verify_single_onion_poison(const smartlist_t *service_list, + const or_options_t *options) +{ + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + if (!rend_service_list) { /* No global HS list. Nothing to see here. */ + return 0; + } + + s_list = rend_service_list; + } else { + s_list = service_list; + } + + int consistent = 1; + SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { + if (service_is_single_onion_poisoned(s) != + rend_service_non_anonymous_mode_enabled(options) && + rend_service_private_key_exists(s)) { + consistent = 0; + } + } SMARTLIST_FOREACH_END(s); + + return consistent ? 0 : -1; +} + +/*** Helper for rend_service_poison_new_single_onion_dirs(). Add a file to + * this hidden service directory that marks it as a single onion service. + * Tor must be in single onion mode before calling this function. + * Returns 0 when a directory is successfully poisoned, or if it is already + * poisoned. Returns -1 on a failure to read the directory or write the poison + * file, or if there is an existing private key file in the directory. (The + * service should have been poisoned when the key was created.) */ +static int +poison_new_single_onion_hidden_service_dir(const rend_service_t *service) +{ + /* We must only poison directories if we're in Single Onion mode */ + tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + + int fd; + int retval = -1; + char *poison_fname = NULL; + + if (rend_service_is_ephemeral(service)) { + log_info(LD_REND, "Ephemeral HS started in non-anonymous mode."); + return 0; + } + + /* Make sure we're only poisoning new hidden service directories */ + if (rend_service_private_key_exists(service)) { + log_warn(LD_BUG, "Tried to single onion poison a service directory after " + "the private key was created."); + return -1; + } + + poison_fname = rend_service_sos_poison_path(service); + + switch (file_status(poison_fname)) { + case FN_DIR: + case FN_ERROR: + log_warn(LD_FS, "Can't read single onion poison file \"%s\"", + poison_fname); + goto done; + case FN_FILE: /* single onion poison file already exists. NOP. */ + case FN_EMPTY: /* single onion poison file already exists. NOP. */ + log_debug(LD_FS, "Tried to re-poison a single onion poisoned file \"%s\"", + poison_fname); + break; + case FN_NOENT: + fd = tor_open_cloexec(poison_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); + if (fd < 0) { + log_warn(LD_FS, "Could not create single onion poison file %s", + poison_fname); + goto done; + } + close(fd); + break; + default: + tor_assert(0); + } + + retval = 0; + + done: + tor_free(poison_fname); + + return retval; +} + +/** We just got launched in Single Onion Mode. That's a non-anoymous + * mode for hidden services; hence we should mark all new hidden service + * directories appropriately so that they are never launched as + * location-private hidden services again. (New directories don't have private + * key files.) + * If a <b>service_list</b> is provided, treat it as the list of hidden + * services (used in unittests). + * Return 0 on success, -1 on fail. */ +int +rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) +{ + /* We must only poison directories if we're in Single Onion mode */ + tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + if (!rend_service_list) { /* No global HS list. Nothing to see here. */ + return 0; + } + + s_list = rend_service_list; + } else { + s_list = service_list; + } + + SMARTLIST_FOREACH_BEGIN(s_list, const rend_service_t *, s) { + if (!rend_service_private_key_exists(s)) { + if (poison_new_single_onion_hidden_service_dir(s) < 0) { + return -1; + } + } + } SMARTLIST_FOREACH_END(s); + + /* The keys for these services are linked to the server IP address */ + log_notice(LD_REND, "The configured onion service directories have been " + "used in single onion mode. They can not be used for anonymous " + "hidden services."); + + return 0; +} + /** Load and/or generate private keys for all hidden services, possibly - * including keys for client authorization. Return 0 on success, -1 on - * failure. */ + * including keys for client authorization. + * If a <b>service_list</b> is provided, treat it as the list of hidden + * services (used in unittests). Otherwise, require that rend_service_list is + * not NULL. + * Return 0 on success, -1 on failure. */ int -rend_service_load_all_keys(void) +rend_service_load_all_keys(const smartlist_t *service_list) { - SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) { + const smartlist_t *s_list; + /* If no special service list is provided, then just use the global one. */ + if (!service_list) { + tor_assert(rend_service_list); + s_list = rend_service_list; + } else { + s_list = service_list; + } + + SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) { if (s->private_key) continue; - log_info(LD_REND, "Loading hidden-service keys from \"%s\"", - s->directory); + log_info(LD_REND, "Loading hidden-service keys from %s", + rend_service_escaped_dir(s)); if (rend_service_load_keys(s) < 0) return -1; @@ -1028,12 +1219,10 @@ 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", - s->directory); - smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"client_keys", - s->directory); + smartlist_add(lst, rend_service_path(s, private_key_fname)); + smartlist_add(lst, rend_service_path(s, hostname_fname)); + smartlist_add(lst, rend_service_path(s, client_keys_fname)); + smartlist_add(lst, rend_service_sos_poison_path(s)); } /** Add to <b>open_lst</b> every filename used by a configured hidden service, @@ -1046,9 +1235,9 @@ 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) { - if (s->directory) { + if (!rend_service_is_ephemeral(s)) { rend_service_add_filenames_to_list(open_lst, s); - smartlist_add(stat_lst, tor_strdup(s->directory)); + smartlist_add_strdup(stat_lst, s->directory); } } SMARTLIST_FOREACH_END(s); } @@ -1077,7 +1266,7 @@ rend_service_derive_key_digests(struct rend_service_t *s) static int rend_service_load_keys(rend_service_t *s) { - char fname[512]; + char *fname = NULL; char buf[128]; cpd_check_t check_opts = CPD_CREATE; @@ -1086,46 +1275,36 @@ rend_service_load_keys(rend_service_t *s) } /* Check/create directory */ if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) { - return -1; + goto err; } #ifndef _WIN32 if (s->dir_group_readable) { /* Only new dirs created get new opts, also enforce group read. */ if (chmod(s->directory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable.", s->directory); + log_warn(LD_FS,"Unable to make %s group-readable.", + rend_service_escaped_dir(s)); } } #endif /* Load key */ - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"private_key",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store key file: \"%s\".", - s->directory); - return -1; - } + fname = rend_service_path(s, private_key_fname); s->private_key = init_key_from_file(fname, 1, LOG_ERR, 0); + if (!s->private_key) - return -1; + goto err; if (rend_service_derive_key_digests(s) < 0) - return -1; + goto err; + tor_free(fname); /* Create service file */ - if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) || - strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname)) - >= sizeof(fname)) { - log_warn(LD_CONFIG, "Directory name too long to store hostname file:" - " \"%s\".", s->directory); - return -1; - } + fname = rend_service_path(s, hostname_fname); tor_snprintf(buf, sizeof(buf),"%s.onion\n", s->service_id); if (write_str_to_file(fname,buf,0)<0) { log_warn(LD_CONFIG, "Could not write onion address to hostname file."); - memwipe(buf, 0, sizeof(buf)); - return -1; + goto err; } #ifndef _WIN32 if (s->dir_group_readable) { @@ -1136,15 +1315,21 @@ rend_service_load_keys(rend_service_t *s) } #endif - memwipe(buf, 0, sizeof(buf)); - /* If client authorization is configured, load or generate keys. */ if (s->auth_type != REND_NO_AUTH) { - if (rend_service_load_auth_keys(s, fname) < 0) - return -1; + if (rend_service_load_auth_keys(s, fname) < 0) { + goto err; + } } - return 0; + int r = 0; + goto done; + err: + r = -1; + done: + memwipe(buf, 0, sizeof(buf)); + tor_free(fname); + return r; } /** Load and/or generate client authorization keys for the hidden service @@ -1154,7 +1339,7 @@ static int rend_service_load_auth_keys(rend_service_t *s, const char *hfname) { int r = 0; - char cfname[512]; + char *cfname = NULL; char *client_keys_str = NULL; strmap_t *parsed_clients = strmap_new(); FILE *cfile, *hfile; @@ -1164,12 +1349,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) char buf[1500]; /* Load client keys and descriptor cookies, if available. */ - if (tor_snprintf(cfname, sizeof(cfname), "%s"PATH_SEPARATOR"client_keys", - s->directory)<0) { - log_warn(LD_CONFIG, "Directory name too long to store client keys " - "file: \"%s\".", s->directory); - goto err; - } + cfname = rend_service_path(s, client_keys_fname); client_keys_str = read_file_to_str(cfname, RFTS_IGNORE_MISSING, NULL); if (client_keys_str) { if (rend_parse_client_keys(parsed_clients, client_keys_str) < 0) { @@ -1323,7 +1503,10 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname) } strmap_free(parsed_clients, rend_authorized_client_strmap_item_free); - memwipe(cfname, 0, sizeof(cfname)); + if (cfname) { + memwipe(cfname, 0, strlen(cfname)); + tor_free(cfname); + } /* Clear stack buffers that held key-derived material. */ memwipe(buf, 0, sizeof(buf)); @@ -1425,6 +1608,31 @@ rend_check_authorization(rend_service_t *service, return 1; } +/* Can this service make a direct connection to ei? + * It must be a single onion service, and the firewall rules must allow ei. */ +static int +rend_service_use_direct_connection(const or_options_t* options, + const extend_info_t* ei) +{ + /* We'll connect directly all reachable addresses, whether preferred or not. + * The prefer_ipv6 argument to fascist_firewall_allows_address_addr is + * ignored, because pref_only is 0. */ + return (rend_service_allow_non_anonymous_connection(options) && + fascist_firewall_allows_address_addr(&ei->addr, ei->port, + FIREWALL_OR_CONNECTION, 0, 0)); +} + +/* Like rend_service_use_direct_connection, but to a node. */ +static int +rend_service_use_direct_connection_node(const or_options_t* options, + const node_t* node) +{ + /* We'll connect directly all reachable addresses, whether preferred or not. + */ + return (rend_service_allow_non_anonymous_connection(options) && + fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, 0)); +} + /****** * Handle cells ******/ @@ -1474,9 +1682,7 @@ rend_service_receive_introduction(origin_circuit_t *circuit, goto err; } -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, options); tor_assert(circuit->rend_data); /* XXX: This is version 2 specific (only one supported). */ rend_pk_digest = (char *) rend_data_get_pk_digest(circuit->rend_data, NULL); @@ -1681,6 +1887,11 @@ rend_service_receive_introduction(origin_circuit_t *circuit, for (i=0;i<MAX_REND_FAILURES;i++) { int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; + /* A Single Onion Service only uses a direct connection if its + * firewall rules permit direct connections to the address. */ + if (rend_service_use_direct_connection(options, rp)) { + flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL; + } launched = circuit_launch_by_extend_info( CIRCUIT_PURPOSE_S_CONNECT_REND, rp, flags); @@ -1792,7 +2003,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro, goto err; } - rp = extend_info_from_node(node, 0); + /* Are we in single onion mode? */ + const int allow_direct = rend_service_allow_non_anonymous_connection( + get_options()); + rp = extend_info_from_node(node, allow_direct); if (!rp) { if (err_msg_out) { tor_asprintf(&err_msg, @@ -1817,6 +2031,10 @@ find_rp_for_intro(const rend_intro_cell_t *intro, goto err; } + /* rp is always set here: extend_info_dup guarantees a non-NULL result, and + * the other cases goto err. */ + tor_assert(rp); + /* 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. */ @@ -2591,6 +2809,10 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc) log_info(LD_REND,"Reattempting rendezvous circuit to '%s'", safe_str(extend_info_describe(oldstate->chosen_exit))); + /* You'd think Single Onion Services would want to retry the rendezvous + * using a direct connection. But if it's blocked by a firewall, or the + * service is IPv6-only, or the rend point avoiding becoming a one-hop + * proxy, we need a 3-hop connection. */ newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, oldstate->chosen_exit, CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); @@ -2619,26 +2841,72 @@ rend_service_launch_establish_intro(rend_service_t *service, rend_intro_point_t *intro) { origin_circuit_t *launched; + int flags = CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL; + const or_options_t *options = get_options(); + extend_info_t *launch_ei = intro->extend_info; + extend_info_t *direct_ei = NULL; + + /* Are we in single onion mode? */ + if (rend_service_allow_non_anonymous_connection(options)) { + /* Do we have a descriptor for the node? + * We've either just chosen it from the consensus, or we've just reviewed + * our intro points to see which ones are still valid, and deleted the ones + * that aren't in the consensus any more. */ + const node_t *node = node_get_by_id(launch_ei->identity_digest); + if (BUG(!node)) { + /* The service has kept an intro point after it went missing from the + * consensus. If we did anything else here, it would be a consensus + * distinguisher. Which are less of an issue for single onion services, + * but still a bug. */ + return -1; + } + /* Can we connect to the node directly? If so, replace launch_ei + * (a multi-hop extend_info) with one suitable for direct connection. */ + if (rend_service_use_direct_connection_node(options, node)) { + direct_ei = extend_info_from_node(node, 1); + if (BUG(!direct_ei)) { + /* rend_service_use_direct_connection_node and extend_info_from_node + * disagree about which addresses on this node are permitted. This + * should never happen. Avoiding the connection is a safe response. */ + return -1; + } + flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL; + launch_ei = direct_ei; + } + } + /* launch_ei is either intro->extend_info, or has been replaced with a valid + * extend_info for single onion service direct connection. */ + tor_assert(launch_ei); + /* We must have the same intro when making a direct connection. */ + tor_assert(tor_memeq(intro->extend_info->identity_digest, + launch_ei->identity_digest, + DIGEST_LEN)); log_info(LD_REND, - "Launching circuit to introduction point %s for service %s", + "Launching circuit to introduction point %s%s%s for service %s", safe_str_client(extend_info_describe(intro->extend_info)), + direct_ei ? " via direct address " : "", + direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "", service->service_id); rep_hist_note_used_internal(time(NULL), 1, 0); ++service->n_intro_circuits_launched; launched = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_ESTABLISH_INTRO, - intro->extend_info, - CIRCLAUNCH_NEED_UPTIME|CIRCLAUNCH_IS_INTERNAL); + launch_ei, flags); if (!launched) { log_info(LD_REND, - "Can't launch circuit to establish introduction at %s.", - safe_str_client(extend_info_describe(intro->extend_info))); + "Can't launch circuit to establish introduction at %s%s%s.", + safe_str_client(extend_info_describe(intro->extend_info)), + direct_ei ? " via direct address " : "", + direct_ei ? safe_str_client(extend_info_describe(direct_ei)) : "" + ); + extend_info_free(direct_ei); return -1; } - /* We must have the same exit node even if cannibalized. */ + /* We must have the same exit node even if cannibalized or direct connection. + */ tor_assert(tor_memeq(intro->extend_info->identity_digest, launched->build_state->chosen_exit->identity_digest, DIGEST_LEN)); @@ -2649,6 +2917,7 @@ rend_service_launch_establish_intro(rend_service_t *service, launched->intro_key = crypto_pk_dup_key(intro->intro_key); if (launched->base_.state == CIRCUIT_STATE_OPEN) rend_service_intro_has_opened(launched); + extend_info_free(direct_ei); return 0; } @@ -2705,9 +2974,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) const char *rend_pk_digest; tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->cpath); tor_assert(circuit->rend_data); /* XXX: This is version 2 specific (only on supported). */ @@ -2774,6 +3041,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) log_info(LD_REND, "Established circuit %u as introduction point for service %s", (unsigned)circuit->base_.n_circ_id, serviceid); + circuit_log_path(LOG_INFO, LD_REND, circuit); /* Use the intro key instead of the service key in ESTABLISH_INTRO. */ crypto_pk_t *intro_key = circuit->intro_key; @@ -2809,8 +3077,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) log_info(LD_GENERAL, "Couldn't send introduction request for service %s on circuit %u", serviceid, (unsigned)circuit->base_.n_circ_id); - reason = END_CIRC_REASON_INTERNAL; - goto err; + goto done; } /* We've attempted to use this circuit */ @@ -2906,9 +3173,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) tor_assert(circuit->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); tor_assert(circuit->cpath); tor_assert(circuit->build_state); -#ifndef NON_ANONYMOUS_MODE_ENABLED - tor_assert(!(circuit->build_state->onehop_tunnel)); -#endif + assert_circ_anonymity_ok(circuit, get_options()); tor_assert(circuit->rend_data); /* XXX: This is version 2 specific (only one supported). */ @@ -2933,6 +3198,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) "Done building circuit %u to rendezvous with " "cookie %s for service %s", (unsigned)circuit->base_.n_circ_id, hexcookie, serviceid); + circuit_log_path(LOG_INFO, LD_REND, circuit); /* Clear the 'in-progress HS circ has timed out' flag for * consistency with what happens on the client side; this line has @@ -2980,8 +3246,7 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit) buf, REND_COOKIE_LEN+DH_KEY_LEN+DIGEST_LEN, circuit->cpath->prev)<0) { log_warn(LD_GENERAL, "Couldn't send RENDEZVOUS1 cell."); - reason = END_CIRC_REASON_INTERNAL; - goto err; + goto done; } crypto_dh_free(hop->rend_dh_handshake_state); @@ -3502,6 +3767,9 @@ rend_consider_services_intro_points(void) int i; time_t now; const or_options_t *options = get_options(); + /* Are we in single onion mode? */ + const int allow_direct = rend_service_allow_non_anonymous_connection( + get_options()); /* List of nodes we need to _exclude_ when choosing a new node to * establish an intro point to. */ smartlist_t *exclude_nodes; @@ -3597,8 +3865,24 @@ rend_consider_services_intro_points(void) router_crn_flags_t flags = CRN_NEED_UPTIME|CRN_NEED_DESC; if (get_options()->AllowInvalid_ & ALLOW_INVALID_INTRODUCTION) flags |= CRN_ALLOW_INVALID; + router_crn_flags_t direct_flags = flags; + direct_flags |= CRN_PREF_ADDR; + direct_flags |= CRN_DIRECT_CONN; + node = router_choose_random_node(exclude_nodes, - options->ExcludeNodes, flags); + options->ExcludeNodes, + allow_direct ? direct_flags : flags); + /* If we are in single onion mode, retry node selection for a 3-hop + * path */ + if (allow_direct && !node) { + log_info(LD_REND, + "Unable to find an intro point that we can connect to " + "directly for %s, falling back to a 3-hop path.", + safe_str_client(service->service_id)); + node = router_choose_random_node(exclude_nodes, + options->ExcludeNodes, flags); + } + if (!node) { log_warn(LD_REND, "We only have %d introduction points established for %s; " @@ -3608,10 +3892,13 @@ rend_consider_services_intro_points(void) n_intro_points_to_open); break; } - /* 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 picking + * it again in the next iteration. */ smartlist_add(exclude_nodes, (void*)node); intro = tor_malloc_zero(sizeof(rend_intro_point_t)); + /* extend_info is for clients, so we want the multi-hop primary ORPort, + * even if we are a single onion service and intend to connect to it + * directly ourselves. */ intro->extend_info = extend_info_from_node(node, 0); intro->intro_key = crypto_pk_new(); const int fail = crypto_pk_generate_key(intro->intro_key); @@ -3657,8 +3944,9 @@ rend_consider_services_upload(time_t now) { int i; rend_service_t *service; - int rendpostperiod = get_options()->RendPostPeriod; - int rendinitialpostdelay = (get_options()->TestingTorNetwork ? + const or_options_t *options = get_options(); + int rendpostperiod = options->RendPostPeriod; + int rendinitialpostdelay = (options->TestingTorNetwork ? MIN_REND_INITIAL_POST_DELAY_TESTING : MIN_REND_INITIAL_POST_DELAY); @@ -3669,6 +3957,12 @@ rend_consider_services_upload(time_t now) * the descriptor is stable before being published. See comment below. */ service->next_upload_time = now + rendinitialpostdelay + crypto_rand_int(2*rendpostperiod); + /* Single Onion Services prioritise availability over hiding their + * startup time, as their IP address is publicly discoverable anyway. + */ + if (rend_service_reveal_startup_time(options)) { + service->next_upload_time = now + rendinitialpostdelay; + } } /* Does every introduction points have been established? */ unsigned int intro_points_ready = @@ -3741,8 +4035,8 @@ rend_service_dump_stats(int severity) for (i=0; i < smartlist_len(rend_service_list); ++i) { service = smartlist_get(rend_service_list, i); - tor_log(severity, LD_GENERAL, "Service configured in \"%s\":", - service->directory); + tor_log(severity, LD_GENERAL, "Service configured in %s:", + rend_service_escaped_dir(service)); for (j=0; j < smartlist_len(service->intro_nodes); ++j) { intro = smartlist_get(service->intro_nodes, j); safe_name = safe_str_client(intro->extend_info->nickname); @@ -3911,3 +4205,51 @@ rend_service_set_connection_addr_port(edge_connection_t *conn, return -2; } +/* Are HiddenServiceSingleHopMode and HiddenServiceNonAnonymousMode consistent? + */ +static int +rend_service_non_anonymous_mode_consistent(const or_options_t *options) +{ + /* !! is used to make these options boolean */ + return (!! options->HiddenServiceSingleHopMode == + !! options->HiddenServiceNonAnonymousMode); +} + +/* Do the options allow onion services to make direct (non-anonymous) + * connections to introduction or rendezvous points? + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + * Returns true if tor is in HiddenServiceSingleHopMode. */ +int +rend_service_allow_non_anonymous_connection(const or_options_t *options) +{ + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return options->HiddenServiceSingleHopMode ? 1 : 0; +} + +/* Do the options allow us to reveal the exact startup time of the onion + * service? + * Single Onion Services prioritise availability over hiding their + * startup time, as their IP address is publicly discoverable anyway. + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + * Returns true if tor is in non-anonymous hidden service mode. */ +int +rend_service_reveal_startup_time(const or_options_t *options) +{ + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return rend_service_non_anonymous_mode_enabled(options); +} + +/* Is non-anonymous mode enabled using the HiddenServiceNonAnonymousMode + * config option? + * Must only be called after options_validate_single_onion() has successfully + * checked onion service option consistency. + */ +int +rend_service_non_anonymous_mode_enabled(const or_options_t *options) +{ + tor_assert(rend_service_non_anonymous_mode_consistent(options)); + return options->HiddenServiceNonAnonymousMode ? 1 : 0; +} + diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 4966cb0302..630191e8b7 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -63,11 +63,68 @@ struct rend_intro_cell_s { uint8_t dh[DH_KEY_LEN]; }; +/** 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. 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 */ + rend_auth_type_t auth_type; /**< Client authorization type or 0 if no client + * authorization is performed. */ + smartlist_t *clients; /**< List of rend_authorized_client_t's of + * clients that may access our service. Can be NULL + * if no client authorization is performed. */ + /* Other fields */ + crypto_pk_t *private_key; /**< Permanent hidden-service key. */ + char service_id[REND_SERVICE_ID_LEN_BASE32+1]; /**< Onion address without + * '.onion' */ + 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 + * established in this period. */ + unsigned int n_intro_points_wanted; /**< Number of intro points this + * service wants to have open. */ + rend_service_descriptor_t *desc; /**< Current hidden service descriptor. */ + time_t desc_is_dirty; /**< Time at which changes to the hidden service + * descriptor content occurred, or 0 if it's + * up-to-date. */ + time_t next_upload_time; /**< Scheduled next hidden service descriptor + * upload time. */ + /** Replay cache for Diffie-Hellman values of INTRODUCE2 cells, to + * detect repeats. Clients may send INTRODUCE1 cells for the same + * rendezvous point through two or more different introduction points; + * when they do, this keeps us from launching multiple simultaneous attempts + * to connect to the same rend point. */ + replaycache_t *accepted_intro_dh_parts; + /** 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; + +STATIC void rend_service_free(rend_service_t *service); +STATIC char *rend_service_sos_poison_path(const rend_service_t *service); + #endif int num_rend_services(void); int rend_config_services(const or_options_t *options, int validate_only); -int rend_service_load_all_keys(void); +int rend_service_load_all_keys(const smartlist_t *service_list); void rend_services_add_filenames_to_lists(smartlist_t *open_lst, smartlist_t *stat_lst); void rend_consider_services_intro_points(void); @@ -108,6 +165,11 @@ void rend_service_port_config_free(rend_service_port_config_t *p); void rend_authorized_client_free(rend_authorized_client_t *client); +int rend_service_list_verify_single_onion_poison( + const smartlist_t *service_list, + const or_options_t *options); +int rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list); + /** Return value from rend_service_add_ephemeral. */ typedef enum { RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */ @@ -131,5 +193,9 @@ void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, const char *service_id, int seconds_valid); void rend_service_desc_has_uploaded(const rend_data_t *rend_data); +int rend_service_allow_non_anonymous_connection(const or_options_t *options); +int rend_service_reveal_startup_time(const or_options_t *options); +int rend_service_non_anonymous_mode_enabled(const or_options_t *options); + #endif diff --git a/src/or/rephist.c b/src/or/rephist.c index 6a54904746..8bcd7396aa 100644 --- a/src/or/rephist.c +++ b/src/or/rephist.c @@ -4,10 +4,74 @@ /** * \file rephist.c - * \brief Basic history and "reputation" functionality to remember + * \brief Basic history and performance-tracking functionality. + * + * Basic history and performance-tracking functionality to remember * which servers have worked in the past, how much bandwidth we've * been using, which ports we tend to want, and so on; further, * exit port statistics, cell statistics, and connection statistics. + * + * The history and information tracked in this module could sensibly be + * divided into several categories: + * + * <ul><li>Statistics used by authorities to remember the uptime and + * stability information about various relays, including "uptime", + * "weighted fractional uptime" and "mean time between failures". + * + * <li>Bandwidth usage history, used by relays to self-report how much + * bandwidth they've used for different purposes over last day or so, + * in order to generate the {dirreq-,}{read,write}-history lines in + * that they publish. + * + * <li>Predicted ports, used by clients to remember how long it's been + * since they opened an exit connection to each given target + * port. Clients use this information in order to try to keep circuits + * open to exit nodes that can connect to the ports that they care + * about. (The predicted ports mechanism also handles predicted circuit + * usage that _isn't_ port-specific, such as resolves, internal circuits, + * and so on.) + * + * <li>Public key operation counters, for tracking how many times we've + * done each public key operation. (This is unmaintained and we should + * remove it.) + * + * <li>Exit statistics by port, used by exits to keep track of the + * number of streams and bytes they've served at each exit port, so they + * can generate their exit-kibibytes-{read,written} and + * exit-streams-opened statistics. + * + * <li>Circuit stats, used by relays instances to tract circuit + * queue fullness and delay over time, and generate cell-processed-cells, + * cell-queued-cells, cell-time-in-queue, and cell-circuits-per-decile + * statistics. + * + * <li>Descriptor serving statistics, used by directory caches to track + * how many descriptors they've served. + * + * <li>Connection statistics, used by relays to track one-way and + * bidirectional connections. + * + * <li>Onion handshake statistics, used by relays to count how many + * TAP and ntor handshakes they've handled. + * + * <li>Hidden service statistics, used by relays to count rendezvous + * traffic and HSDir-stored descriptors. + * + * <li>Link protocol statistics, used by relays to count how many times + * each link protocol has been used. + * + * </ul> + * + * The entry points for this module are scattered throughout the + * codebase. Sending data, receiving data, connecting to a relay, + * losing a connection to a relay, and so on can all trigger a change in + * our current stats. Relays also invoke this module in order to + * extract their statistics when building routerinfo and extrainfo + * objects in router.c. + * + * TODO: This module should be broken up. + * + * (The "rephist" name originally stood for "reputation and history". ) **/ #include "or.h" @@ -2650,7 +2714,9 @@ rep_hist_desc_stats_write(time_t now) return start_of_served_descs_stats_interval + WRITE_STATS_INTERVAL; } -/* DOCDOC rep_hist_note_desc_served */ +/** Called to note that we've served a given descriptor (by + * digest). Incrememnts the count of descriptors served, and the number + * of times we've served this descriptor. */ void rep_hist_note_desc_served(const char * desc) { diff --git a/src/or/replaycache.c b/src/or/replaycache.c index 23a1737b18..8290fa6964 100644 --- a/src/or/replaycache.c +++ b/src/or/replaycache.c @@ -1,10 +1,22 @@ /* Copyright (c) 2012-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -/* +/** * \file replaycache.c * * \brief Self-scrubbing replay cache for rendservice.c + * + * To prevent replay attacks, hidden services need to recognize INTRODUCE2 + * cells that they've already seen, and drop them. If they didn't, then + * sending the same INTRODUCE2 cell over and over would force the hidden + * service to make a huge number of circuits to the same rendezvous + * point, aiding traffic analysis. + * + * (It's not that simple, actually. We only check for replays in the + * RSA-encrypted portion of the handshake, since the rest of the handshake is + * malleable.) + * + * This module is used from rendservice.c. */ #define REPLAYCACHE_PRIVATE diff --git a/src/or/router.c b/src/or/router.c index e9961d4594..e45f233634 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -23,6 +23,7 @@ #include "networkstatus.h" #include "nodelist.h" #include "policies.h" +#include "protover.h" #include "relay.h" #include "rephist.h" #include "router.h" @@ -36,8 +37,23 @@ /** * \file router.c - * \brief OR functionality, including key maintenance, generating - * and uploading server descriptors, retrying OR connections. + * \brief Miscellaneous relay functionality, including RSA key maintenance, + * generating and uploading server descriptors, picking an address to + * advertise, and so on. + * + * This module handles the job of deciding whether we are a Tor relay, and if + * so what kind. (Mostly through functions like server_mode() that inspect an + * or_options_t, but in some cases based on our own capabilities, such as when + * we are deciding whether to be a directory cache in + * router_has_bandwidth_to_be_dirserver().) + * + * Also in this module are the functions to generate our own routerinfo_t and + * extrainfo_t, and to encode those to signed strings for upload to the + * directory authorities. + * + * This module also handles key maintenance for RSA and Curve25519-ntor keys, + * and for our TLS context. (These functions should eventually move to + * routerkeys.c along with the code that handles Ed25519 keys now.) **/ /************************************************************/ @@ -452,7 +468,8 @@ init_key_from_file(const char *fname, int generate, int severity, goto error; } } else { - log_info(LD_GENERAL, "No key found in \"%s\"", fname); + tor_log(severity, LD_GENERAL, "No key found in \"%s\"", fname); + goto error; } return prkey; case FN_FILE: @@ -560,7 +577,7 @@ load_authority_keyset(int legacy, crypto_pk_t **key_out, fname = get_datadir_fname2("keys", legacy ? "legacy_signing_key" : "authority_signing_key"); - signing_key = init_key_from_file(fname, 0, LOG_INFO, 0); + signing_key = init_key_from_file(fname, 0, LOG_ERR, 0); if (!signing_key) { log_warn(LD_DIR, "No version 3 directory key found in %s", fname); goto done; @@ -2091,8 +2108,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) tor_addr_family(&p->addr) == AF_INET6) { /* 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); + const int default_auth = using_default_dir_authorities(options); if (! tor_addr_is_internal(&p->addr, 0) || ! default_auth) { ipv6_orport = p; break; @@ -2124,6 +2140,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) get_platform_str(platform, sizeof(platform)); ri->platform = tor_strdup(platform); + ri->protocol_list = tor_strdup(protover_get_supported_protocols()); + /* compute ri->bandwidthrate as the min of various options */ ri->bandwidthrate = get_effective_bwrate(options); @@ -2177,7 +2195,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) log_warn(LD_CONFIG, "There is a router named \"%s\" in my " "declared family, but that isn't a legal nickname. " "Skipping it.", escaped(name)); - smartlist_add(warned_nonexistent_family, tor_strdup(name)); + smartlist_add_strdup(warned_nonexistent_family, name); } if (is_legal) { smartlist_add(ri->declared_family, name); @@ -2616,6 +2634,7 @@ router_dump_router_to_string(routerinfo_t *router, char *ed_cert_line = NULL; char *rsa_tap_cc_line = NULL; char *ntor_cc_line = NULL; + char *proto_line = NULL; /* Make sure the identity key matches the one in the routerinfo. */ if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) { @@ -2780,6 +2799,12 @@ router_dump_router_to_string(routerinfo_t *router, } } + if (router->protocol_list) { + tor_asprintf(&proto_line, "proto %s\n", router->protocol_list); + } else { + proto_line = tor_strdup(""); + } + address = tor_dup_ip(router->addr); chunks = smartlist_new(); @@ -2789,7 +2814,7 @@ router_dump_router_to_string(routerinfo_t *router, "%s" "%s" "platform %s\n" - "protocols Link 1 2 Circuit 1\n" + "%s" "published %s\n" "fingerprint %s\n" "uptime %ld\n" @@ -2806,6 +2831,7 @@ router_dump_router_to_string(routerinfo_t *router, ed_cert_line ? ed_cert_line : "", extra_or_address ? extra_or_address : "", router->platform, + proto_line, published, fingerprint, stats_n_seconds_working, @@ -2836,11 +2862,15 @@ router_dump_router_to_string(routerinfo_t *router, (const char *)router->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE); smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf); + } else { + /* Authorities will start rejecting relays without ntor keys in 0.2.9 */ + log_err(LD_BUG, "A relay must have an ntor onion key"); + goto err; } /* Write the exit policy to the end of 's'. */ if (!router->exit_policy || !smartlist_len(router->exit_policy)) { - smartlist_add(chunks, tor_strdup("reject *:*\n")); + smartlist_add_strdup(chunks, "reject *:*\n"); } else if (router->exit_policy) { char *exit_policy = router_dump_exit_policy_to_string(router,1,0); @@ -2862,12 +2892,12 @@ router_dump_router_to_string(routerinfo_t *router, if (decide_to_advertise_begindir(options, router->supports_tunnelled_dir_requests)) { - smartlist_add(chunks, tor_strdup("tunnelled-dir-server\n")); + smartlist_add_strdup(chunks, "tunnelled-dir-server\n"); } /* Sign the descriptor with Ed25519 */ if (emit_ed_sigs) { - smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + smartlist_add_strdup(chunks, "router-sig-ed25519 "); crypto_digest_smartlist_prefix(digest, DIGEST256_LEN, ED_DESC_SIGNATURE_PREFIX, chunks, "", DIGEST_SHA256); @@ -2883,7 +2913,7 @@ router_dump_router_to_string(routerinfo_t *router, } /* Sign the descriptor with RSA */ - smartlist_add(chunks, tor_strdup("router-signature\n")); + smartlist_add_strdup(chunks, "router-signature\n"); crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1); @@ -2898,7 +2928,7 @@ router_dump_router_to_string(routerinfo_t *router, } /* include a last '\n' */ - smartlist_add(chunks, tor_strdup("\n")); + smartlist_add_strdup(chunks, "\n"); output = smartlist_join_strings(chunks, "", 0, NULL); @@ -2938,6 +2968,7 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(rsa_tap_cc_line); tor_free(ntor_cc_line); tor_free(extra_info_line); + tor_free(proto_line); return output; } @@ -3155,13 +3186,13 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, if (should_record_bridge_info(options) && write_stats_to_extrainfo) { const char *bridge_stats = geoip_get_bridge_stats_extrainfo(now); if (bridge_stats) { - smartlist_add(chunks, tor_strdup(bridge_stats)); + smartlist_add_strdup(chunks, bridge_stats); } } if (emit_ed_sigs) { char sha256_digest[DIGEST256_LEN]; - smartlist_add(chunks, tor_strdup("router-sig-ed25519 ")); + smartlist_add_strdup(chunks, "router-sig-ed25519 "); crypto_digest_smartlist_prefix(sha256_digest, DIGEST256_LEN, ED_DESC_SIGNATURE_PREFIX, chunks, "", DIGEST_SHA256); @@ -3176,7 +3207,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, smartlist_add_asprintf(chunks, "%s\n", buf); } - smartlist_add(chunks, tor_strdup("router-signature\n")); + smartlist_add_strdup(chunks, "router-signature\n"); s = smartlist_join_strings(chunks, "", 0, NULL); while (strlen(s) > MAX_EXTRAINFO_UPLOAD_SIZE - DIROBJ_MAX_SIG_LEN) { @@ -3211,7 +3242,7 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo, "descriptor."); goto err; } - smartlist_add(chunks, tor_strdup(sig)); + smartlist_add_strdup(chunks, sig); tor_free(s); s = smartlist_join_strings(chunks, "", 0, NULL); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 060ffd8753..8d9a1328b8 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -5,8 +5,13 @@ * \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.) + * set of keypairs necessary to be an OR. + * + * The keys handled here now are the Ed25519 keys that Tor relays use to sign + * descriptors, authenticate themselves on links, and identify one another + * uniquely. Other keys are maintained in router.c and rendservice.c. + * + * (TODO: The keys in router.c should go here too.) */ #include "or.h" @@ -19,6 +24,7 @@ #define ENC_KEY_HEADER "Boxed Ed25519 key" #define ENC_KEY_TAG "master" +/* DOCDOC */ static ssize_t do_getpass(const char *prompt, char *buf, size_t buflen, int twice, const or_options_t *options) @@ -48,7 +54,7 @@ do_getpass(const char *prompt, char *buf, size_t buflen, size_t p2len = strlen(prompt) + 1; if (p2len < sizeof(msg)) p2len = sizeof(msg); - prompt2 = tor_malloc(strlen(prompt)+1); + prompt2 = tor_malloc(p2len); memset(prompt2, ' ', p2len); memcpy(prompt2 + p2len - sizeof(msg), msg, sizeof(msg)); @@ -85,6 +91,7 @@ do_getpass(const char *prompt, char *buf, size_t buflen, return length; } +/* DOCDOC */ int read_encrypted_secret_key(ed25519_secret_key_t *out, const char *fname) @@ -157,6 +164,7 @@ read_encrypted_secret_key(ed25519_secret_key_t *out, return r; } +/* DOCDOC */ int write_encrypted_secret_key(const ed25519_secret_key_t *key, const char *fname) @@ -200,6 +208,7 @@ write_encrypted_secret_key(const ed25519_secret_key_t *key, return r; } +/* DOCDOC */ static int write_secret_key(const ed25519_secret_key_t *key, int encrypted, const char *fname, @@ -927,7 +936,18 @@ load_ed_keys(const or_options_t *options, time_t now) return -1; } -/* DOCDOC */ +/** + * Retrieve our currently-in-use Ed25519 link certificate and id certificate, + * and, if they would expire soon (based on the time <b>now</b>, generate new + * certificates (without embedding the public part of the signing key inside). + * + * The signed_key from the expiring certificate will be used to sign the new + * key within newly generated X509 certificate. + * + * Returns -1 upon error. Otherwise, returns 0 upon success (either when the + * current certificate is still valid, or when a new certificate was + * successfully generated). + */ int generate_ed_link_cert(const or_options_t *options, time_t now) { @@ -967,6 +987,17 @@ generate_ed_link_cert(const or_options_t *options, time_t now) #undef SET_KEY #undef SET_CERT +/** + * Return 1 if any of the following are true: + * + * - if one of our Ed25519 signing, auth, or link certificates would expire + * soon w.r.t. the time <b>now</b>, + * - if we do not currently have a link certificate, or + * - if our cached Ed25519 link certificate is not same as the one we're + * currently using. + * + * Otherwise, returns 0. + */ int should_make_new_ed_keys(const or_options_t *options, const time_t now) { @@ -997,6 +1028,61 @@ should_make_new_ed_keys(const or_options_t *options, const time_t now) #undef EXPIRES_SOON +#ifdef TOR_UNIT_TESTS +/* Helper for unit tests: populate the ed25519 keys without saving or + * loading */ +void +init_mock_ed_keys(const crypto_pk_t *rsa_identity_key) +{ + routerkeys_free_all(); + +#define MAKEKEY(k) \ + k = tor_malloc_zero(sizeof(*k)); \ + if (ed25519_keypair_generate(k, 0) < 0) { \ + log_warn(LD_BUG, "Couldn't make a keypair"); \ + goto err; \ + } + MAKEKEY(master_identity_key); + MAKEKEY(master_signing_key); + MAKEKEY(current_auth_key); +#define MAKECERT(cert, signing, signed_, type, flags) \ + cert = tor_cert_create(signing, \ + type, \ + &signed_->pubkey, \ + time(NULL), 86400, \ + flags); \ + if (!cert) { \ + log_warn(LD_BUG, "Couldn't make a %s certificate!", #cert); \ + goto err; \ + } + + MAKECERT(signing_key_cert, + master_identity_key, master_signing_key, CERT_TYPE_ID_SIGNING, + CERT_FLAG_INCLUDE_SIGNING_KEY); + MAKECERT(auth_key_cert, + master_signing_key, current_auth_key, CERT_TYPE_SIGNING_AUTH, 0); + + if (generate_ed_link_cert(get_options(), time(NULL)) < 0) { + log_warn(LD_BUG, "Couldn't make link certificate"); + goto err; + } + + rsa_ed_crosscert_len = tor_make_rsa_ed25519_crosscert( + &master_identity_key->pubkey, + rsa_identity_key, + time(NULL)+86400, + &rsa_ed_crosscert); + + return; + + err: + routerkeys_free_all(); + tor_assert_nonfatal_unreached(); +} +#undef MAKEKEY +#undef MAKECERT +#endif + const ed25519_public_key_t * get_master_identity_key(void) { @@ -1005,6 +1091,16 @@ get_master_identity_key(void) return &master_identity_key->pubkey; } +#ifdef TOR_UNIT_TESTS +/* only exists for the unit tests, since otherwise the identity key + * should be used to sign nothing but the signing key. */ +const ed25519_keypair_t * +get_master_identity_keypair(void) +{ + return master_identity_key; +} +#endif + const ed25519_keypair_t * get_master_signing_keypair(void) { @@ -1139,9 +1235,12 @@ routerkeys_free_all(void) tor_cert_free(signing_key_cert); tor_cert_free(link_cert_cert); tor_cert_free(auth_key_cert); + tor_free(rsa_ed_crosscert); master_identity_key = master_signing_key = NULL; current_auth_key = NULL; signing_key_cert = link_cert_cert = auth_key_cert = NULL; + rsa_ed_crosscert = NULL; // redundant + rsa_ed_crosscert_len = 0; } diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index be9b19aea8..307a1cd234 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -73,5 +73,10 @@ int write_encrypted_secret_key(const ed25519_secret_key_t *out, void routerkeys_free_all(void); +#ifdef TOR_UNIT_TESTS +const ed25519_keypair_t *get_master_identity_keypair(void); +void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key); +#endif + #endif diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 1773f1d05c..c99d22ed41 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -9,6 +9,85 @@ * \brief Code to * maintain and access the global list of routerinfos for known * servers. + * + * A "routerinfo_t" object represents a single self-signed router + * descriptor, as generated by a Tor relay in order to tell the rest of + * the world about its keys, address, and capabilities. An + * "extrainfo_t" object represents an adjunct "extra-info" object, + * certified by a corresponding router descriptor, reporting more + * information about the relay that nearly all users will not need. + * + * Most users will not use router descriptors for most relays. Instead, + * they use the information in microdescriptors and in the consensus + * networkstatus. + * + * Right now, routerinfo_t objects are used in these ways: + * <ul> + * <li>By clients, in order to learn about bridge keys and capabilities. + * (Bridges aren't listed in the consensus networkstatus, so they + * can't have microdescriptors.) + * <li>By relays, since relays want more information about other relays + * than they can learn from microdescriptors. (TODO: Is this still true?) + * <li>By authorities, which receive them and use them to generate the + * consensus and the microdescriptors. + * <li>By all directory caches, which download them in case somebody + * else wants them. + * </ul> + * + * Routerinfos are mostly created by parsing them from a string, in + * routerparse.c. We store them to disk on receiving them, and + * periodically discard the ones we don't need. On restarting, we + * re-read them from disk. (This also applies to extrainfo documents, if + * we are configured to fetch them.) + * + * In order to keep our list of routerinfos up-to-date, we periodically + * check whether there are any listed in the latest consensus (or in the + * votes from other authorities, if we are an authority) that we don't + * have. (This also applies to extrainfo documents, if we are + * configured to fetch them.) + * + * Almost nothing in Tor should use a routerinfo_t to refer directly to + * a relay; instead, almost everything should use node_t (implemented in + * nodelist.c), which provides a common interface to routerinfo_t, + * routerstatus_t, and microdescriptor_t. + * + * <br> + * + * This module also has some of the functions used for choosing random + * nodes according to different rules and weights. Historically, they + * were all in this module. Now, they are spread across this module, + * nodelist.c, and networkstatus.c. (TODO: Fix that.) + * + * <br> + * + * (For historical reasons) this module also contains code for handling + * the list of fallback directories, the list of directory authorities, + * and the list of authority certificates. + * + * For the directory authorities, we have a list containing the public + * identity key, and contact points, for each authority. The + * authorities receive descriptors from relays, and publish consensuses, + * descriptors, and microdescriptors. This list is pre-configured. + * + * Fallback directories are well-known, stable, but untrusted directory + * caches that clients which have not yet bootstrapped can use to get + * their first networkstatus consensus, in order to find out where the + * Tor network really is. This list is pre-configured in + * fallback_dirs.inc. Every authority also serves as a fallback. + * + * Both fallback directories and directory authorities are are + * represented by a dir_server_t. + * + * Authority certificates are signed with authority identity keys; they + * are used to authenticate shorter-term authority signing keys. We + * fetch them when we find a consensus or a vote that has been signed + * with a signing key we don't recognize. We cache them on disk and + * load them on startup. Authority operators generate them with the + * "tor-gencert" utility. + * + * TODO: Authority certificates should be a separate module. + * + * TODO: dir_server_t stuff should be in a separate module. **/ #define ROUTERLIST_PRIVATE @@ -46,6 +125,9 @@ /****************************************************************************/ +/* Typed wrappers for different digestmap types; used to avoid type + * confusion. */ + DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t) DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t) DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t) @@ -800,7 +882,9 @@ static const char *BAD_SIGNING_KEYS[] = { NULL, }; -/* DOCDOC */ +/** Return true iff <b>cert</b> authenticates some atuhority signing key + * which, because of the old openssl heartbleed vulnerability, should + * never be trusted. */ int authority_cert_is_blacklisted(const authority_cert_t *cert) { @@ -845,7 +929,8 @@ authority_certs_fetch_resource_impl(const char *resource, const routerstatus_t *rs) { const or_options_t *options = get_options(); - int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0); + int get_via_tor = purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0, + resource); /* Make sure bridge clients never connect to anything but a bridge */ if (options->UseBridges) { @@ -1118,7 +1203,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, int need_plus = 0; smartlist_t *fps = smartlist_new(); - smartlist_add(fps, tor_strdup("fp/")); + smartlist_add_strdup(fps, "fp/"); SMARTLIST_FOREACH_BEGIN(missing_id_digests, const char *, d) { char *fp = NULL; @@ -1158,7 +1243,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, int need_plus = 0; smartlist_t *fp_pairs = smartlist_new(); - smartlist_add(fp_pairs, tor_strdup("fp-sk/")); + smartlist_add_strdup(fp_pairs, "fp-sk/"); SMARTLIST_FOREACH_BEGIN(missing_cert_digests, const fp_pair_t *, d) { char *fp_pair = NULL; @@ -1953,9 +2038,9 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, !router_supports_extrainfo(node->identity, is_trusted_extrainfo)) continue; /* Don't make the same node a guard twice */ - if (for_guard && node->using_as_guard) { - continue; - } + if (for_guard && is_node_used_as_guard(node)) { + continue; + } /* Ensure that a directory guard is actually a guard node. */ if (for_guard && !node->is_possible_guard) { continue; @@ -2260,10 +2345,17 @@ 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 */ + /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ + if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) + continue; + /* Don't choose nodes if we are certain they can't do ntor. */ + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + continue; + /* Choose a node with an OR address that matches the firewall rules */ if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, pref_addr)) + !fascist_firewall_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) continue; smartlist_add(sl, (void *)node); @@ -3096,6 +3188,7 @@ routerinfo_free(routerinfo_t *router) tor_free(router->cache_info.signed_descriptor_body); tor_free(router->nickname); tor_free(router->platform); + tor_free(router->protocol_list); tor_free(router->contact_info); if (router->onion_pkey) crypto_pk_free(router->onion_pkey); @@ -5497,6 +5590,47 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey, return r; } +/* Does ri have a valid ntor onion key? + * Valid ntor onion keys exist and have at least one non-zero byte. */ +int +routerinfo_has_curve25519_onion_key(const routerinfo_t *ri) +{ + if (!ri) { + return 0; + } + + if (!ri->onion_curve25519_pkey) { + return 0; + } + + if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key, + CURVE25519_PUBKEY_LEN)) { + return 0; + } + + return 1; +} + +/* Is rs running a tor version known to support EXTEND2 cells? + * If allow_unknown_versions is true, return true if we can't tell + * (from a versions line or a protocols line) whether it supports extend2 + * cells. + * Otherwise, return false if the version is unknown. */ +int +routerstatus_version_supports_extend2_cells(const routerstatus_t *rs, + int allow_unknown_versions) +{ + if (!rs) { + return allow_unknown_versions; + } + + if (!rs->protocols_known) { + return allow_unknown_versions; + } + + return rs->supports_extend2_cells; +} + /** Assert that the internal representation of <b>rl</b> is * self-consistent. */ void diff --git a/src/or/routerlist.h b/src/or/routerlist.h index 72ab6d9bf3..606e9085ce 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -206,6 +206,9 @@ int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri, extrainfo_t *ei, signed_descriptor_t *sd, const char **msg); +int routerinfo_has_curve25519_onion_key(const routerinfo_t *ri); +int routerstatus_version_supports_extend2_cells(const routerstatus_t *rs, + int allow_unknown_versions); void routerlist_assert_ok(const routerlist_t *rl); const char *esc_router_info(const routerinfo_t *router); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index ef6273ba91..5bc2d39579 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -6,7 +6,51 @@ /** * \file routerparse.c - * \brief Code to parse and validate router descriptors and directories. + * \brief Code to parse and validate router descriptors, consenus directories, + * and similar objects. + * + * The objects parsed by this module use a common text-based metaformat, + * documented in dir-spec.txt in torspec.git. This module is itself divided + * into two major kinds of function: code to handle the metaformat, and code + * to convert from particular instances of the metaformat into the + * objects that Tor uses. + * + * The generic parsing code works by calling a table-based tokenizer on the + * input string. Each token corresponds to a single line with a token, plus + * optional arguments on that line, plus an optional base-64 encoded object + * after that line. Each token has a definition in a table of token_rule_t + * entries that describes how many arguments it can take, whether it takes an + * object, how many times it may appear, whether it must appear first, and so + * on. + * + * The tokenizer function tokenize_string() converts its string input into a + * smartlist full of instances of directory_token_t, according to a provided + * table of token_rule_t. + * + * The generic parts of this module additionally include functions for + * finding the start and end of signed information inside a signed object, and + * computing the digest that will be signed. + * + * There are also functions for saving objects to disk that have caused + * parsing to fail. + * + * The specific parts of this module describe conversions between + * particular lists of directory_token_t and particular objects. The + * kinds of objects that can be parsed here are: + * <ul> + * <li>router descriptors (managed from routerlist.c) + * <li>extra-info documents (managed from routerlist.c) + * <li>microdescriptors (managed from microdesc.c) + * <li>vote and consensus networkstatus documents, and the routerstatus_t + * objects that they comprise (managed from networkstatus.c) + * <li>detached-signature objects used by authorities for gathering + * signatures on the networkstatus consensus (managed from dirvote.c) + * <li>authority key certificates (managed from routerlist.c) + * <li>hidden service descriptors (managed from rendcommon.c and rendcache.c) + * </ul> + * + * For no terribly good reason, the functions to <i>generate</i> signatures on + * the above directory objects are also in this module. **/ #define ROUTERPARSE_PRIVATE @@ -18,6 +62,7 @@ #include "dirvote.h" #include "parsecommon.h" #include "policies.h" +#include "protover.h" #include "rendcommon.h" #include "router.h" #include "routerlist.h" @@ -54,6 +99,7 @@ static token_rule_t routerdesc_token_table[] = { T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ), T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ), T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ), + T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ), T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ), T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ), @@ -130,6 +176,7 @@ static token_rule_t rtrstatus_token_table[] = { T01("w", K_W, ARGS, NO_OBJ ), T0N("m", K_M, CONCAT_ARGS, NO_OBJ ), T0N("id", K_ID, GE(2), NO_OBJ ), + T01("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), END_OF_TABLE }; @@ -207,6 +254,14 @@ static token_rule_t networkstatus_token_table[] = { T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ), T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ), T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ), + T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), CERTIFICATE_MEMBERS @@ -248,6 +303,15 @@ static token_rule_t networkstatus_consensus_token_table[] = { T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ), T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ), + T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS, + CONCAT_ARGS, NO_OBJ ), + END_OF_TABLE }; @@ -1756,12 +1820,13 @@ router_parse_entry_from_string(const char *s, const char *end, ed25519_checkable_t check[3]; int check_ok[3]; - if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + time_t expires = TIME_MAX; + if (tor_cert_get_checkable_sig(&check[0], cert, NULL, &expires) < 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) { + ntor_cc_cert, &ntor_cc_pk, &expires) < 0) { log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert."); goto err; } @@ -1791,10 +1856,7 @@ router_parse_entry_from_string(const char *s, const char *end, } /* 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; + router->cert_expiration_time = expires; } } @@ -1820,6 +1882,10 @@ router_parse_entry_from_string(const char *s, const char *end, router->platform = tor_strdup(tok->args[0]); } + if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { + router->protocol_list = tor_strdup(tok->args[0]); + } + if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) { router->contact_info = tor_strdup(tok->args[0]); } @@ -1872,7 +1938,7 @@ router_parse_entry_from_string(const char *s, const char *end, escaped(tok->args[i])); goto err; } - smartlist_add(router->declared_family, tor_strdup(tok->args[i])); + smartlist_add_strdup(router->declared_family, tok->args[i]); } } @@ -2104,7 +2170,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end, ed25519_checkable_t check[2]; int check_ok[2]; - if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) { + if (tor_cert_get_checkable_sig(&check[0], cert, NULL, NULL) < 0) { log_err(LD_BUG, "Couldn't create 'checkable' for cert."); goto err; } @@ -2459,7 +2525,7 @@ routerstatus_parse_guardfraction(const char *guardfraction_str, * * Parse according to the syntax used by the consensus flavor <b>flav</b>. **/ -static routerstatus_t * +STATIC routerstatus_t * routerstatus_parse_entry_from_string(memarea_t *area, const char **s, smartlist_t *tokens, networkstatus_t *vote, @@ -2573,6 +2639,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, } } } else if (tok) { + /* This is a consensus, not a vote. */ int i; for (i=0; i < tok->n_args; ++i) { if (!strcmp(tok->args[i], "Exit")) @@ -2603,14 +2670,28 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->is_v2_dir = 1; } } + /* These are implied true by having been included in a consensus made + * with a given method */ + rs->is_flagged_running = 1; /* Starting with consensus method 4. */ + if (consensus_method >= MIN_METHOD_FOR_EXCLUDING_INVALID_NODES) + rs->is_valid = 1; + } + int found_protocol_list = 0; + if ((tok = find_opt_by_keyword(tokens, K_PROTO))) { + found_protocol_list = 1; + rs->protocols_known = 1; + rs->supports_extend2_cells = + protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2); } if ((tok = find_opt_by_keyword(tokens, K_V))) { tor_assert(tok->n_args == 1); - rs->version_known = 1; - if (strcmpstart(tok->args[0], "Tor ")) { - } else { - rs->version_supports_extend2_cells = + if (!strcmpstart(tok->args[0], "Tor ") && !found_protocol_list) { + /* We only do version checks like this in the case where + * the version is a "Tor" version, and where there is no + * list of protocol versions that we should be looking at instead. */ + rs->supports_extend2_cells = tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha"); + rs->protocols_known = 1; } if (vote_rs) { vote_rs->version = tor_strdup(tok->args[0]); @@ -2693,6 +2774,10 @@ routerstatus_parse_entry_from_string(memarea_t *area, } } } + if (t->tp == K_PROTO) { + tor_assert(t->n_args == 1); + vote_rs->protocols = tor_strdup(t->args[0]); + } } SMARTLIST_FOREACH_END(t); } else if (flav == FLAV_MICRODESC) { tok = find_opt_by_keyword(tokens, K_M); @@ -3356,9 +3441,9 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS); if (tok) { for (i=0; i < tok->n_args; ++i) - smartlist_add(ns->supported_methods, tor_strdup(tok->args[i])); + smartlist_add_strdup(ns->supported_methods, tok->args[i]); } else { - smartlist_add(ns->supported_methods, tor_strdup("1")); + smartlist_add_strdup(ns->supported_methods, "1"); } } else { tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD); @@ -3373,6 +3458,15 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } } + if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_CLIENT_PROTOCOLS))) + ns->recommended_client_protocols = tor_strdup(tok->args[0]); + if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_RELAY_PROTOCOLS))) + ns->recommended_relay_protocols = tor_strdup(tok->args[0]); + if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_CLIENT_PROTOCOLS))) + ns->required_client_protocols = tor_strdup(tok->args[0]); + if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_RELAY_PROTOCOLS))) + ns->required_relay_protocols = tor_strdup(tok->args[0]); + tok = find_by_keyword(tokens, K_VALID_AFTER); if (parse_iso_time(tok->args[0], &ns->valid_after)) goto err; @@ -3431,7 +3525,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, ns->package_lines = smartlist_new(); if (package_lst) { SMARTLIST_FOREACH(package_lst, directory_token_t *, t, - smartlist_add(ns->package_lines, tor_strdup(t->args[0]))); + smartlist_add_strdup(ns->package_lines, t->args[0])); } smartlist_free(package_lst); } @@ -3440,7 +3534,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, ns->known_flags = smartlist_new(); inorder = 1; for (i = 0; i < tok->n_args; ++i) { - smartlist_add(ns->known_flags, tor_strdup(tok->args[i])); + smartlist_add_strdup(ns->known_flags, tok->args[i]); if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) { log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]); inorder = 0; @@ -3492,7 +3586,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, } tor_free(last_kwd); last_kwd = tor_strndup(tok->args[i], eq_pos); - smartlist_add(ns->net_params, tor_strdup(tok->args[i])); + smartlist_add_strdup(ns->net_params, tok->args[i]); } if (!inorder) { log_warn(LD_DIR, "params not in order"); @@ -3735,7 +3829,7 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out, log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i])); goto err; } - smartlist_add(ns->weight_params, tor_strdup(tok->args[i])); + smartlist_add_strdup(ns->weight_params, tok->args[i]); } } @@ -4661,7 +4755,7 @@ microdescs_parse_from_string(const char *s, const char *eos, escaped(tok->args[i])); goto next; } - smartlist_add(md->family, tor_strdup(tok->args[i])); + smartlist_add_strdup(md->family, tok->args[i]); } } diff --git a/src/or/routerparse.h b/src/or/routerparse.h index 81ef724945..9a3fadca1f 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -112,6 +112,14 @@ MOCK_DECL(STATIC dumped_desc_t *, dump_desc_populate_one_file, STATIC void dump_desc_populate_fifo_from_directory(const char *dirname); STATIC void dump_desc(const char *desc, const char *type); STATIC void dump_desc_fifo_cleanup(void); +struct memarea_t; +STATIC routerstatus_t *routerstatus_parse_entry_from_string( + struct memarea_t *area, + const char **s, smartlist_t *tokens, + networkstatus_t *vote, + vote_routerstatus_t *vote_rs, + int consensus_method, + consensus_flavor_t flav); #endif #define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1" diff --git a/src/or/routerset.c b/src/or/routerset.c index f260914f4b..4182dbc5c4 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -9,6 +9,20 @@ * * \brief Functions and structures to handle set-type selection of routers * by name, ID, address, etc. + * + * This module implements the routerset_t data structure, whose purpose + * is to specify a set of relays based on a list of their identities or + * properties. Routersets can restrict relays by IP address mask, + * identity fingerprint, country codes, and nicknames (deprecated). + * + * Routersets are typically used for user-specified restrictions, and + * are created by invoking routerset_new and routerset_parse from + * config.c and confparse.c. To use a routerset, invoke one of + * routerset_contains_...() functions , or use + * routerstatus_get_all_nodes() / routerstatus_subtract_nodes() to + * manipulate a smartlist of node_t pointers. + * + * Country-code restrictions are implemented in geoip.c. */ #define ROUTERSET_PRIVATE @@ -248,12 +262,12 @@ routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set) geoip_get_country("A1") >= 0; if (add_unknown) { - smartlist_add(set->country_names, tor_strdup("??")); - smartlist_add(set->list, tor_strdup("{??}")); + smartlist_add_strdup(set->country_names, "??"); + smartlist_add_strdup(set->list, "{??}"); } if (add_a1) { - smartlist_add(set->country_names, tor_strdup("a1")); - smartlist_add(set->list, tor_strdup("{a1}")); + smartlist_add_strdup(set->country_names, "a1"); + smartlist_add_strdup(set->list, "{a1}"); } if (add_unknown || add_a1) { diff --git a/src/or/scheduler.c b/src/or/scheduler.c index 49ac1b939a..033e6d119c 100644 --- a/src/or/scheduler.c +++ b/src/or/scheduler.c @@ -1,11 +1,6 @@ /* * Copyright (c) 2013-2016, The Tor Project, Inc. */ /* See LICENSE for licensing information */ -/** - * \file scheduler.c - * \brief Relay scheduling system - **/ - #include "or.h" #define TOR_CHANNEL_INTERNAL_ /* For channel_flush_some_cells() */ @@ -32,66 +27,102 @@ static uint32_t sched_q_high_water = 32768; static uint32_t sched_max_flush_cells = 16; -/* - * Write scheduling works by keeping track of which channels can +/** + * \file scheduler.c + * \brief Channel scheduling system: decides which channels should send and + * receive when. + * + * This module implements a scheduler algorithm, to decide + * which channels should send/receive when. + * + * The earliest versions of Tor approximated a kind of round-robin system + * among active connections, but only approximated it. + * + * Now, write scheduling works by keeping track of which channels can * accept cells, and have cells to write. From the scheduler's perspective, * a channel can be in four possible states: * - * 1.) Not open for writes, no cells to send - * - Not much to do here, and the channel will have scheduler_state == - * SCHED_CHAN_IDLE - * - Transitions from: - * - Open for writes/has cells by simultaneously draining all circuit + * <ol> + * <li> + * Not open for writes, no cells to send. + * <ul><li> Not much to do here, and the channel will have scheduler_state + * == SCHED_CHAN_IDLE + * <li> Transitions from: + * <ul> + * <li>Open for writes/has cells by simultaneously draining all circuit * queues and filling the output buffer. - * - Transitions to: - * - Not open for writes/has cells by arrival of cells on an attached + * </ul> + * <li> Transitions to: + * <ul> + * <li> Not open for writes/has cells by arrival of cells on an attached * circuit (this would be driven from append_cell_to_circuit_queue()) - * - Open for writes/no cells by a channel type specific path; + * <li> Open for writes/no cells by a channel type specific path; * driven from connection_or_flushed_some() for channel_tls_t. + * </ul> + * </ul> * - * 2.) Open for writes, no cells to send - * - Not much here either; this will be the state an idle but open channel - * can be expected to settle in. It will have scheduler_state == - * SCHED_CHAN_WAITING_FOR_CELLS - * - Transitions from: - * - Not open for writes/no cells by flushing some of the output + * <li> Open for writes, no cells to send + * <ul> + * <li>Not much here either; this will be the state an idle but open + * channel can be expected to settle in. It will have scheduler_state + * == SCHED_CHAN_WAITING_FOR_CELLS + * <li> Transitions from: + * <ul> + * <li>Not open for writes/no cells by flushing some of the output * buffer. - * - Open for writes/has cells by the scheduler moving cells from + * <li>Open for writes/has cells by the scheduler moving cells from * circuit queues to channel output queue, but not having enough * to fill the output queue. - * - Transitions to: - * - Open for writes/has cells by arrival of new cells on an attached + * </ul> + * <li> Transitions to: + * <ul> + * <li>Open for writes/has cells by arrival of new cells on an attached * circuit, in append_cell_to_circuit_queue() + * </ul> + * </ul> * - * 3.) Not open for writes, cells to send - * - This is the state of a busy circuit limited by output bandwidth; + * <li>Not open for writes, cells to send + * <ul> + * <li>This is the state of a busy circuit limited by output bandwidth; * cells have piled up in the circuit queues waiting to be relayed. * The channel will have scheduler_state == SCHED_CHAN_WAITING_TO_WRITE. - * - Transitions from: - * - Not open for writes/no cells by arrival of cells on an attached + * <li> Transitions from: + * <ul> + * <li>Not open for writes/no cells by arrival of cells on an attached * circuit - * - Open for writes/has cells by filling an output buffer without + * <li> Open for writes/has cells by filling an output buffer without * draining all cells from attached circuits - * - Transitions to: - * - Opens for writes/has cells by draining some of the output buffer + * </ul> + * <li> Transitions to: + * <ul> + * <li>Opens for writes/has cells by draining some of the output buffer * via the connection_or_flushed_some() path (for channel_tls_t). + * </ul> + * </ul> * - * 4.) Open for writes, cells to send - * - This connection is ready to relay some cells and waiting for + * <li>Open for writes, cells to send + * <ul> + * <li>This connection is ready to relay some cells and waiting for * the scheduler to choose it. The channel will have scheduler_state == * SCHED_CHAN_PENDING. - * - Transitions from: - * - Not open for writes/has cells by the connection_or_flushed_some() + * <li>Transitions from: + * <ul> + * <li> Not open for writes/has cells by the connection_or_flushed_some() * path - * - Open for writes/no cells by the append_cell_to_circuit_queue() + * <li> Open for writes/no cells by the append_cell_to_circuit_queue() * path - * - Transitions to: - * - Not open for writes/no cells by draining all circuit queues and - * simultaneously filling the output buffer. - * - Not open for writes/has cells by writing enough cells to fill the + * </ul> + * <li> Transitions to: + * <ul> + * <li>Not open for writes/no cells by draining all circuit queues and + * simultaneously filling the output buffer. + * <li>Not open for writes/has cells by writing enough cells to fill the * output buffer - * - Open for writes/no cells by draining all attached circuit queues + * <li>Open for writes/no cells by draining all attached circuit queues * without also filling the output buffer + * </ul> + * </ul> + * </ol> * * Other event-driven parts of the code move channels between these scheduling * states by calling scheduler functions; the scheduler only runs on open-for- diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 19564f5924..5f6b03f1ba 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -201,7 +201,7 @@ verify_commit_and_reveal(const sr_commit_t *commit) if (fast_memneq(received_hashed_reveal, commit->hashed_reveal, sizeof(received_hashed_reveal))) { log_warn(LD_BUG, "SR: Received reveal value from authority %s " - "does't match the commit value.", + "doesn't match the commit value.", sr_commit_get_rsa_fpr(commit)); goto invalid; } @@ -578,8 +578,8 @@ commit_is_authoritative(const sr_commit_t *commit, tor_assert(commit); tor_assert(voter_key); - return !memcmp(commit->rsa_identity, voter_key, - sizeof(commit->rsa_identity)); + return fast_memeq(commit->rsa_identity, voter_key, + sizeof(commit->rsa_identity)); } /* Decide if the newly received <b>commit</b> should be kept depending on diff --git a/src/or/statefile.c b/src/or/statefile.c index adf9d9f038..8fa4324b25 100644 --- a/src/or/statefile.c +++ b/src/or/statefile.c @@ -9,6 +9,23 @@ * * \brief Handles parsing and encoding the persistent 'state' file that carries * miscellaneous persistent state between Tor invocations. + * + * This 'state' file is a typed key-value store that allows multiple + * entries for the same key. It follows the same metaformat as described + * in confparse.c, and uses the same code to read and write itself. + * + * The state file is most suitable for small values that don't change too + * frequently. For values that become very large, we typically use a separate + * file -- for example, see how we handle microdescriptors, by storing them in + * a separate file with a journal. + * + * The current state is accessed via get_or_state(), which returns a singleton + * or_state_t object. Functions that change it should call + * or_state_mark_dirty() to ensure that it will get written to disk. + * + * The or_state_save() function additionally calls various functioens + * throughout Tor that might want to flush more state to the the disk, + * including some in rephist.c, entrynodes.c, circuitstats.c, hibernate.c. */ #define STATEFILE_PRIVATE diff --git a/src/or/status.c b/src/or/status.c index 749cee4edf..fce6a10157 100644 --- a/src/or/status.c +++ b/src/or/status.c @@ -3,7 +3,13 @@ /** * \file status.c - * \brief Keep status information and log the heartbeat messages. + * \brief Collect status information and log heartbeat messages. + * + * This module is responsible for implementing the heartbeat log messages, + * which periodically inform users and operators about basic facts to + * do with their Tor instance. The log_heartbeat() function, invoked from + * main.c, is the principle entry point. It collects data from elsewhere + * in Tor, and logs it in a human-readable format. **/ #define STATUS_PRIVATE diff --git a/src/or/tor_main.c b/src/or/tor_main.c index 21fbe3efb5..d67eda2ac9 100644 --- a/src/or/tor_main.c +++ b/src/or/tor_main.c @@ -17,8 +17,10 @@ const char tor_git_revision[] = /** * \file tor_main.c - * \brief Stub module containing a main() function. Allows unit - * test binary to link against main.c. + * \brief Stub module containing a main() function. + * + * We keep the main function in a separate module so that the unit + * tests, which have their own main()s, can link against main.c. **/ int tor_main(int argc, char *argv[]); diff --git a/src/or/torcert.c b/src/or/torcert.c index a6a33c675a..852def9ef6 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -6,8 +6,27 @@ * * \brief Implementation for ed25519-signed certificates as used in the Tor * protocol. + * + * This certificate format is designed to be simple and compact; it's + * documented in tor-spec.txt in the torspec.git repository. All of the + * certificates in this format are signed with an Ed25519 key; the + * contents themselves may be another Ed25519 key, a digest of a + * RSA key, or some other material. + * + * In this module there is also support for a crooss-certification of + * Ed25519 identities using (older) RSA1024 identities. + * + * Tor uses other types of certificate too, beyond those described in this + * module. Notably, our use of TLS requires us to touch X.509 certificates, + * even though sensible people would stay away from those. Our X.509 + * certificates are represented with tor_x509_cert_t, and implemented in + * tortls.c. We also have a separate certificate type that authorities + * use to authenticate their RSA signing keys with their RSA identity keys: + * that one is authority_cert_t, and it's mostly handled in routerlist.c. */ +#include "or.h" +#include "config.h" #include "crypto.h" #include "torcert.h" #include "ed25519_cert.h" @@ -137,7 +156,11 @@ tor_cert_parse(const uint8_t *encoded, const size_t len) cert->encoded_len = len; memcpy(cert->signed_key.pubkey, parsed->certified_key, 32); - cert->valid_until = parsed->exp_field * 3600; + const int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600; + if (valid_until_64 > TIME_MAX) + cert->valid_until = TIME_MAX - 1; + else + cert->valid_until = (time_t) valid_until_64; cert->cert_type = parsed->cert_type; for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) { @@ -164,11 +187,17 @@ tor_cert_parse(const uint8_t *encoded, const size_t len) } /** Fill in <b>checkable_out</b> with the information needed to check - * the signature on <b>cert</b> with <b>pubkey</b>. */ + * the signature on <b>cert</b> with <b>pubkey</b>. + * + * On success, if <b>expiration_out</b> is provided, and it is some time + * _after_ the expiration time of this certificate, set it to the + * expiration time of this certificate. + */ int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, const tor_cert_t *cert, - const ed25519_public_key_t *pubkey) + const ed25519_public_key_t *pubkey, + time_t *expiration_out) { if (! pubkey) { if (cert->signing_key_included) @@ -185,6 +214,10 @@ tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, memcpy(checkable_out->signature.sig, cert->encoded + signed_len, ED25519_SIG_LEN); + if (expiration_out) { + *expiration_out = MIN(*expiration_out, cert->valid_until); + } + return 0; } @@ -199,14 +232,15 @@ tor_cert_checksig(tor_cert_t *cert, { ed25519_checkable_t checkable; int okay; + time_t expires = TIME_MAX; - if (now && now > cert->valid_until) { - cert->cert_expired = 1; + if (tor_cert_get_checkable_sig(&checkable, cert, pubkey, &expires) < 0) return -1; - } - if (tor_cert_get_checkable_sig(&checkable, cert, pubkey) < 0) + if (now && now > expires) { + cert->cert_expired = 1; return -1; + } if (ed25519_checksig_batch(&okay, &checkable, 1) < 0) { cert->sig_bad = 1; @@ -255,6 +289,8 @@ tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2) return tor_cert_eq(cert1, cert2); } +#define RSA_ED_CROSSCERT_PREFIX "Tor TLS RSA/Ed25519 cross-certificate" + /** 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 @@ -279,11 +315,21 @@ tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); tor_assert(sz > 0 && sz <= alloc_sz); + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, RSA_ED_CROSSCERT_PREFIX, + strlen(RSA_ED_CROSSCERT_PREFIX)); + const int signed_part_len = 32 + 4; + crypto_digest_add_bytes(d, (char*)res, signed_part_len); + + uint8_t digest[DIGEST256_LEN]; + crypto_digest_get_digest(d, (char*)digest, sizeof(digest)); + crypto_digest_free(d); + 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); + (char*)digest, sizeof(digest)); tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key)); tor_assert(siglen <= UINT8_MAX); cc->sig_len = siglen; @@ -295,3 +341,309 @@ tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, return sz; } +/** + * Check whether the <b>crosscert_len</b> byte certificate in <b>crosscert</b> + * is in fact a correct cross-certification of <b>master_key</b> using + * the RSA key <b>rsa_id_key</b>. + * + * Also reject the certificate if it expired before + * <b>reject_if_expired_before</b>. + * + * Return 0 on success, negative on failure. + */ +int +rsa_ed25519_crosscert_check(const uint8_t *crosscert, + const size_t crosscert_len, + const crypto_pk_t *rsa_id_key, + const ed25519_public_key_t *master_key, + const time_t reject_if_expired_before) +{ + rsa_ed_crosscert_t *cc = NULL; + int rv; + +#define ERR(code, s) \ + do { \ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ + "Received a bad RSA->Ed25519 crosscert: %s", \ + (s)); \ + rv = (code); \ + goto err; \ + } while (0) + + if (BUG(crypto_pk_keysize(rsa_id_key) > PK_BYTES)) + return -1; + + if (BUG(!crosscert)) + return -1; + + ssize_t parsed_len = rsa_ed_crosscert_parse(&cc, crosscert, crosscert_len); + if (parsed_len < 0 || crosscert_len != (size_t)parsed_len) { + ERR(-2, "Unparseable or overlong crosscert"); + } + + if (tor_memneq(rsa_ed_crosscert_getarray_ed_key(cc), + master_key->pubkey, + ED25519_PUBKEY_LEN)) { + ERR(-3, "Crosscert did not match Ed25519 key"); + } + + const uint32_t expiration_date = rsa_ed_crosscert_get_expiration(cc); + const uint64_t expiration_time = expiration_date * 3600; + + if (reject_if_expired_before < 0 || + expiration_time < (uint64_t)reject_if_expired_before) { + ERR(-4, "Crosscert is expired"); + } + + const uint8_t *eos = rsa_ed_crosscert_get_end_of_signed(cc); + const uint8_t *sig = rsa_ed_crosscert_getarray_sig(cc); + const uint8_t siglen = rsa_ed_crosscert_get_sig_len(cc); + tor_assert(eos >= crosscert); + tor_assert((size_t)(eos - crosscert) <= crosscert_len); + tor_assert(siglen == rsa_ed_crosscert_getlen_sig(cc)); + + /* Compute the digest */ + uint8_t digest[DIGEST256_LEN]; + crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); + crypto_digest_add_bytes(d, RSA_ED_CROSSCERT_PREFIX, + strlen(RSA_ED_CROSSCERT_PREFIX)); + crypto_digest_add_bytes(d, (char*)crosscert, eos-crosscert); + crypto_digest_get_digest(d, (char*)digest, sizeof(digest)); + crypto_digest_free(d); + + /* Now check the signature */ + uint8_t signed_[PK_BYTES]; + int signed_len = crypto_pk_public_checksig(rsa_id_key, + (char*)signed_, sizeof(signed_), + (char*)sig, siglen); + if (signed_len < DIGEST256_LEN) { + ERR(-5, "Bad signature, or length of signed data not as expected"); + } + + if (tor_memneq(digest, signed_, DIGEST256_LEN)) { + ERR(-6, "The signature was good, but it didn't match the data"); + } + + rv = 0; + err: + rsa_ed_crosscert_free(cc); + return rv; +} + +/** Construct and return a new empty or_handshake_certs object */ +or_handshake_certs_t * +or_handshake_certs_new(void) +{ + return tor_malloc_zero(sizeof(or_handshake_certs_t)); +} + +/** Release all storage held in <b>certs</b> */ +void +or_handshake_certs_free(or_handshake_certs_t *certs) +{ + if (!certs) + return; + + tor_x509_cert_free(certs->auth_cert); + tor_x509_cert_free(certs->link_cert); + tor_x509_cert_free(certs->id_cert); + + tor_cert_free(certs->ed_id_sign); + tor_cert_free(certs->ed_sign_link); + tor_cert_free(certs->ed_sign_auth); + tor_free(certs->ed_rsa_crosscert); + + memwipe(certs, 0xBD, sizeof(*certs)); + tor_free(certs); +} + +#undef ERR +#define ERR(s) \ + do { \ + log_fn(severity, LD_PROTOCOL, \ + "Received a bad CERTS cell: %s", \ + (s)); \ + return 0; \ + } while (0) + +int +or_handshake_certs_rsa_ok(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now) +{ + tor_x509_cert_t *link_cert = certs->link_cert; + tor_x509_cert_t *auth_cert = certs->auth_cert; + tor_x509_cert_t *id_cert = certs->id_cert; + + if (certs->started_here) { + if (! (id_cert && link_cert)) + ERR("The certs we wanted (ID, Link) were missing"); + if (! tor_tls_cert_matches_key(tls, link_cert)) + ERR("The link certificate didn't match the TLS public key"); + if (! tor_tls_cert_is_valid(severity, link_cert, id_cert, now, 0)) + ERR("The link certificate was not valid"); + if (! tor_tls_cert_is_valid(severity, id_cert, id_cert, now, 1)) + ERR("The ID certificate was not valid"); + } else { + if (! (id_cert && auth_cert)) + ERR("The certs we wanted (ID, Auth) were missing"); + if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, auth_cert, id_cert, now, 1)) + ERR("The authentication certificate was not valid"); + if (! tor_tls_cert_is_valid(LOG_PROTOCOL_WARN, id_cert, id_cert, now, 1)) + ERR("The ID certificate was not valid"); + } + + return 1; +} + +/** Check all the ed25519 certificates in <b>certs</b> against each other, and + * against the peer certificate in <b>tls</b> if appropriate. On success, + * return 0; on failure, return a negative value and warn at level + * <b>severity</b> */ +int +or_handshake_certs_ed25519_ok(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now) +{ + ed25519_checkable_t check[10]; + unsigned n_checkable = 0; + time_t expiration = TIME_MAX; + +#define ADDCERT(cert, pk) \ + do { \ + tor_assert(n_checkable < ARRAY_LENGTH(check)); \ + if (tor_cert_get_checkable_sig(&check[n_checkable++], cert, pk, \ + &expiration) < 0) \ + ERR("Could not get checkable cert."); \ + } while (0) + + if (! certs->ed_id_sign || !certs->ed_id_sign->signing_key_included) { + ERR("No Ed25519 signing key"); + } + ADDCERT(certs->ed_id_sign, NULL); + + if (certs->started_here) { + if (! certs->ed_sign_link) + ERR("No Ed25519 link key"); + { + /* check for a match with the TLS cert. */ + tor_x509_cert_t *peer_cert = tor_tls_get_peer_cert(tls); + if (BUG(!peer_cert)) { + /* This is a bug, because if we got to this point, we are a connection + * that was initiated here, and we completed a TLS handshake. The + * other side *must* have given us a certificate! */ + ERR("No x509 peer cert"); // LCOV_EXCL_LINE + } + const common_digests_t *peer_cert_digests = + tor_x509_cert_get_cert_digests(peer_cert); + int okay = tor_memeq(peer_cert_digests->d[DIGEST_SHA256], + certs->ed_sign_link->signed_key.pubkey, + DIGEST256_LEN); + tor_x509_cert_free(peer_cert); + if (!okay) + ERR("Link certificate does not match TLS certificate"); + } + + ADDCERT(certs->ed_sign_link, &certs->ed_id_sign->signed_key); + + } else { + if (! certs->ed_sign_auth) + ERR("No Ed25519 link authentication key"); + ADDCERT(certs->ed_sign_auth, &certs->ed_id_sign->signed_key); + } + + if (expiration < now) { + ERR("At least one certificate expired."); + } + + /* Okay, we've gotten ready to check all the Ed25519 certificates. + * Now, we are going to check the RSA certificate's cross-certification + * with the ED certificates. + * + * FFFF In the future, we might want to make this optional. + */ + + tor_x509_cert_t *rsa_id_cert = certs->id_cert; + if (!rsa_id_cert) { + ERR("Missing legacy RSA ID certificate"); + } + if (! tor_tls_cert_is_valid(severity, rsa_id_cert, rsa_id_cert, now, 1)) { + ERR("The legacy RSA ID certificate was not valid"); + } + if (! certs->ed_rsa_crosscert) { + ERR("Missing RSA->Ed25519 crosscert"); + } + crypto_pk_t *rsa_id_key = tor_tls_cert_get_key(rsa_id_cert); + if (!rsa_id_key) { + ERR("RSA ID cert had no RSA key"); + } + + if (rsa_ed25519_crosscert_check(certs->ed_rsa_crosscert, + certs->ed_rsa_crosscert_len, + rsa_id_key, + &certs->ed_id_sign->signing_key, + now) < 0) { + crypto_pk_free(rsa_id_key); + ERR("Invalid RSA->Ed25519 crosscert"); + } + crypto_pk_free(rsa_id_key); + rsa_id_key = NULL; + + /* FFFF We could save a little time in the client case by queueing + * this batch to check it later, along with the signature from the + * AUTHENTICATE cell. That will change our data flow a bit, though, + * so I say "postpone". */ + + if (ed25519_checksig_batch(NULL, check, n_checkable) < 0) { + ERR("At least one Ed25519 certificate was badly signed"); + } + + return 1; +} + +/** + * Check the Ed certificates and/or the RSA certificates, as appropriate. If + * we obtained an Ed25519 identity, set *ed_id_out. If we obtained an RSA + * identity, set *rs_id_out. Otherwise, set them both to NULL. + */ +void +or_handshake_certs_check_both(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now, + const ed25519_public_key_t **ed_id_out, + const common_digests_t **rsa_id_out) +{ + tor_assert(ed_id_out); + tor_assert(rsa_id_out); + + *ed_id_out = NULL; + *rsa_id_out = NULL; + + if (certs->ed_id_sign) { + if (or_handshake_certs_ed25519_ok(severity, certs, tls, now)) { + tor_assert(certs->ed_id_sign); + tor_assert(certs->id_cert); + + *ed_id_out = &certs->ed_id_sign->signing_key; + *rsa_id_out = tor_x509_cert_get_id_digests(certs->id_cert); + + /* If we reached this point, we did not look at any of the + * subsidiary RSA certificates, so we'd better just remove them. + */ + tor_x509_cert_free(certs->link_cert); + tor_x509_cert_free(certs->auth_cert); + certs->link_cert = certs->auth_cert = NULL; + } + /* We do _not_ fall through here. If you provided us Ed25519 + * certificates, we expect to verify them! */ + } else { + /* No ed25519 keys given in the CERTS cell */ + if (or_handshake_certs_rsa_ok(severity, certs, tls, now)) { + *rsa_id_out = tor_x509_cert_get_id_digests(certs->id_cert); + } + } +} + diff --git a/src/or/torcert.h b/src/or/torcert.h index b1e26a9fc0..4bd816f4a4 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -60,8 +60,9 @@ 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); + const tor_cert_t *out, + const ed25519_public_key_t *pubkey, + time_t *expiration_out); int tor_cert_checksig(tor_cert_t *cert, const ed25519_public_key_t *pubkey, time_t now); @@ -74,6 +75,28 @@ 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); +int rsa_ed25519_crosscert_check(const uint8_t *crosscert, + const size_t crosscert_len, + const crypto_pk_t *rsa_id_key, + const ed25519_public_key_t *master_key, + const time_t reject_if_expired_before); + +or_handshake_certs_t *or_handshake_certs_new(void); +void or_handshake_certs_free(or_handshake_certs_t *certs); +int or_handshake_certs_rsa_ok(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now); +int or_handshake_certs_ed25519_ok(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now); +void or_handshake_certs_check_both(int severity, + or_handshake_certs_t *certs, + tor_tls_t *tls, + time_t now, + const ed25519_public_key_t **ed_id_out, + const common_digests_t **rsa_id_out); #endif diff --git a/src/or/transports.c b/src/or/transports.c index be77c871c4..614b28c168 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -430,7 +430,7 @@ add_transport_to_proxy(const char *transport, managed_proxy_t *mp) { tor_assert(mp->transports_to_launch); if (!smartlist_contains_string(mp->transports_to_launch, transport)) - smartlist_add(mp->transports_to_launch, tor_strdup(transport)); + smartlist_add_strdup(mp->transports_to_launch, transport); } /** Called when a SIGHUP occurs. Returns true if managed proxy @@ -1322,7 +1322,7 @@ create_managed_proxy_environment(const managed_proxy_t *mp) tor_free(state_tmp); } - smartlist_add(envs, tor_strdup("TOR_PT_MANAGED_TRANSPORT_VER=1")); + smartlist_add_strdup(envs, "TOR_PT_MANAGED_TRANSPORT_VER=1"); { char *transports_to_launch = |