diff options
Diffstat (limited to 'src')
49 files changed, 945 insertions, 391 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index 458067af4d..bf2f49ead4 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -2189,6 +2189,23 @@ options_act,(const or_options_t *old_options)) } } + /* Validate that we actually have a configured transport for a Bridge line + * that has one. This is done here because we require the bridge and + * transport to be added to the global list before doing the validation. + * + * In an ideal world, pt_parse_transport_line() would actually return a + * transport_t object so we could inspect it and thus do this step at + * validation time. */ + SMARTLIST_FOREACH_BEGIN(bridge_list_get(), const bridge_info_t *, bi) { + const char *bi_transport_name = bridget_get_transport_name(bi); + if (bi_transport_name && (!transport_get_by_name(bi_transport_name) && + !managed_proxy_has_transport(bi_transport_name))) { + log_warn(LD_CONFIG, "Bridge line with transport %s is missing a " + "ClientTransportPlugin line", bi_transport_name); + return -1; + } + } SMARTLIST_FOREACH_END(bi); + if (options_act_server_transport(old_options) < 0) return -1; @@ -2466,6 +2483,7 @@ static const struct { .command=CMD_DUMP_CONFIG, .quiet=QUIET_SILENT }, { .name="--list-fingerprint", + .takes_argument=ARGUMENT_OPTIONAL, .command=CMD_LIST_FINGERPRINT }, { .name="--keygen", .command=CMD_KEYGEN }, @@ -2584,8 +2602,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors) parsed_cmdline_free(result); return NULL; } - } else if (want_arg == ARGUMENT_OPTIONAL && is_last) { + } else if (want_arg == ARGUMENT_OPTIONAL && + /* optional arguments may never start with '-'. */ + (is_last || argv[i+1][0] == '-')) { arg = tor_strdup(""); + want_arg = ARGUMENT_NONE; // prevent skipping the next flag. } else { arg = (want_arg != ARGUMENT_NONE) ? tor_strdup(argv[i+1]) : tor_strdup(""); @@ -3274,7 +3295,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) } #else /* defined(HAVE_SYS_UN_H) */ if (options->ControlSocketsGroupWritable && !options->ControlSocket) { - *msg = tor_strdup("Setting ControlSocketGroupWritable without setting " + *msg = tor_strdup("Setting ControlSocketsGroupWritable without setting " "a ControlSocket makes no sense."); return -1; } @@ -4485,6 +4506,25 @@ options_init_from_torrc(int argc, char **argv) if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); + printf("Tor is running on %s with Libevent %s, " + "%s %s, Zlib %s, Liblzma %s, Libzstd %s and %s %s as libc.\n", + get_uname(), + tor_libevent_get_version_str(), + crypto_get_library_name(), + crypto_get_library_version_string(), + tor_compress_supports_method(ZLIB_METHOD) ? + tor_compress_version_str(ZLIB_METHOD) : "N/A", + tor_compress_supports_method(LZMA_METHOD) ? + tor_compress_version_str(LZMA_METHOD) : "N/A", + tor_compress_supports_method(ZSTD_METHOD) ? + tor_compress_version_str(ZSTD_METHOD) : "N/A", + tor_libc_get_name() ? + tor_libc_get_name() : "Unknown", + tor_libc_get_version_str()); + printf("Tor compiled with %s version %s\n", + strcmp(COMPILER_VENDOR, "gnu") == 0? + COMPILER:COMPILER_VENDOR, COMPILER_VERSION); + return 1; } @@ -5934,13 +5974,12 @@ port_parse_config(smartlist_t *out, char *unix_socket_path = NULL; port_cfg_t *cfg = NULL; bool addr_is_explicit = false; - int family = -1; - - /* Parse default address. This can fail for Unix socket for instance so - * family can be -1 and the default_addr will be made UNSPEC. */ tor_addr_t default_addr = TOR_ADDR_NULL; + + /* Parse default address. This can fail for Unix socket so the default_addr + * will simply be made UNSPEC. */ if (defaultaddr) { - family = tor_addr_parse(&default_addr, defaultaddr); + tor_addr_parse(&default_addr, defaultaddr); } /* If there's no FooPort, then maybe make a default one. */ @@ -6018,7 +6057,6 @@ port_parse_config(smartlist_t *out, port = 1; } else if (!strcasecmp(addrport, "auto")) { port = CFG_AUTO_PORT; - tor_assert(family >= 0); tor_addr_copy(&addr, &default_addr); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); @@ -6032,21 +6070,15 @@ port_parse_config(smartlist_t *out, tor_free(addrtmp); } else { /* Try parsing integer port before address, because, who knows? - "9050" might be a valid address. */ + * "9050" might be a valid address. */ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); if (ok) { - tor_assert(family >= 0); tor_addr_copy(&addr, &default_addr); } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { if (ptmp == 0) { log_warn(LD_CONFIG, "%sPort line has address but no port", portname); goto err; } - if (family != -1 && tor_addr_family(&addr) != family) { - /* This means we are parsing another ORPort family but we are - * attempting to find the default address' family ORPort. */ - goto ignore; - } port = ptmp; addr_is_explicit = true; } else { diff --git a/src/app/include.am b/src/app/include.am index b81fad0276..2e2180deca 100644 --- a/src/app/include.am +++ b/src/app/include.am @@ -14,7 +14,8 @@ src_app_tor_SOURCES = src/app/main/tor_main.c # This seems to matter nowhere but on windows, but I assure you that it # matters a lot there, and is quite hard to debug if you forget to do it. -src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ +src_app_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ + @TOR_LDFLAGS_libevent@ @TOR_STATIC_LDFLAGS@ src_app_tor_LDADD = libtor.a \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @@ -26,7 +27,8 @@ if COVERAGE_ENABLED src_app_tor_cov_SOURCES = $(src_app_tor_SOURCES) src_app_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS) src_app_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) -src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) @TOR_LDFLAGS_libevent@ +src_app_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ + @TOR_LDFLAGS_libevent@ @TOR_STATIC_LDFLAGS@ src_app_tor_cov_LDADD = src/test/libtor-testing.a \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_SHLWAPI@ @TOR_LIB_GDI@ \ diff --git a/src/app/main/main.c b/src/app/main/main.c index 589d365add..e7ffb31b4f 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -58,6 +58,7 @@ #include "feature/stats/rephist.h" #include "lib/compress/compress.h" #include "lib/buf/buffers.h" +#include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_s2k.h" #include "lib/net/resolve.h" @@ -735,29 +736,52 @@ tor_remove_file(const char *filename) static int do_list_fingerprint(void) { - char buf[FINGERPRINT_LEN+1]; + const or_options_t *options = get_options(); + const char *arg = options->command_arg; + char rsa[FINGERPRINT_LEN + 1]; crypto_pk_t *k; - const char *nickname = get_options()->Nickname; + const ed25519_public_key_t *edkey; + const char *nickname = options->Nickname; sandbox_disable_getaddrinfo_cache(); - if (!server_mode(get_options())) { + + bool show_rsa = !strcmp(arg, "") || !strcmp(arg, "rsa"); + bool show_ed25519 = !strcmp(arg, "ed25519"); + if (!show_rsa && !show_ed25519) { + log_err(LD_GENERAL, + "If you give a key type, you must specify 'rsa' or 'ed25519'. Exiting."); + return -1; + } + + if (!server_mode(options)) { log_err(LD_GENERAL, "Clients don't have long-term identity keys. Exiting."); return -1; } tor_assert(nickname); if (init_keys() < 0) { - log_err(LD_GENERAL,"Error initializing keys; exiting."); + log_err(LD_GENERAL, "Error initializing keys; exiting."); return -1; } if (!(k = get_server_identity_key())) { - log_err(LD_GENERAL,"Error: missing identity key."); + log_err(LD_GENERAL, "Error: missing RSA identity key."); + return -1; + } + if (crypto_pk_get_fingerprint(k, rsa, 1) < 0) { + log_err(LD_BUG, "Error computing RSA fingerprint"); return -1; } - if (crypto_pk_get_fingerprint(k, buf, 1)<0) { - log_err(LD_BUG, "Error computing fingerprint"); + if (!(edkey = get_master_identity_key())) { + log_err(LD_GENERAL,"Error: missing ed25519 identity key."); return -1; } - printf("%s %s\n", nickname, buf); + if (show_rsa) { + printf("%s %s\n", nickname, rsa); + } + if (show_ed25519) { + char ed25519[ED25519_BASE64_LEN + 1]; + digest256_to_base64(ed25519, (const char *) edkey->pubkey); + printf("%s %s\n", nickname, ed25519); + } return 0; } @@ -1080,6 +1104,7 @@ sandbox_init_filter(void) OPEN_DATADIR2_SUFFIX("stats", "buffer-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "conn-stats", ".tmp"); OPEN_DATADIR2_SUFFIX("stats", "hidserv-stats", ".tmp"); + OPEN_DATADIR2_SUFFIX("stats", "hidserv-v3-stats", ".tmp"); OPEN_DATADIR("approved-routers"); OPEN_DATADIR_SUFFIX("fingerprint", ".tmp"); @@ -1105,6 +1130,7 @@ sandbox_init_filter(void) RENAME_SUFFIX2("stats", "buffer-stats", ".tmp"); RENAME_SUFFIX2("stats", "conn-stats", ".tmp"); RENAME_SUFFIX2("stats", "hidserv-stats", ".tmp"); + RENAME_SUFFIX2("stats", "hidserv-v3-stats", ".tmp"); RENAME_SUFFIX("hashed-fingerprint", ".tmp"); RENAME_SUFFIX("router-stability", ".tmp"); diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index 3d551c4ba8..c0c56a8b77 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -948,7 +948,6 @@ connection_free_minimal(connection_t *conn) connection_or_clear_identity(TO_OR_CONN(conn)); } if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) { - connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn)); tor_free(TO_OR_CONN(conn)->ext_or_conn_id); tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash); tor_free(TO_OR_CONN(conn)->ext_or_transport); @@ -1718,13 +1717,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } - /* Force IPv4 and IPv6 traffic on for non-SOCKSPorts. - * Forcing options on isn't a good idea, see #32994 and #33607. */ - if (type != CONN_TYPE_AP_LISTENER) { - lis_conn->entry_cfg.ipv4_traffic = 1; - lis_conn->entry_cfg.ipv6_traffic = 1; - } - if (connection_add(conn) < 0) { /* no space, forget it */ log_warn(LD_NET,"connection_add for listener failed. Giving up."); goto err; @@ -5824,7 +5816,6 @@ connection_free_all(void) /* Unlink everything from the identity map. */ connection_or_clear_identity_map(); - connection_or_clear_ext_or_id_map(); /* Clear out our list of broken connections */ clear_broken_connection_map(0); diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index 6abfe0ff39..71ab78c51f 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -1937,7 +1937,11 @@ write_stats_file_callback(time_t now, const or_options_t *options) next_time_to_write_stats_files = next_write; } if (options->HiddenServiceStatistics) { - time_t next_write = rep_hist_hs_stats_write(now); + time_t next_write = rep_hist_hs_stats_write(now, false); + if (next_write && next_write < next_time_to_write_stats_files) + next_time_to_write_stats_files = next_write; + + next_write = rep_hist_hs_stats_write(now, true); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } diff --git a/src/core/or/circuitlist.h b/src/core/or/circuitlist.h index fd7e22e4c0..3178e6cd0d 100644 --- a/src/core/or/circuitlist.h +++ b/src/core/or/circuitlist.h @@ -114,7 +114,9 @@ #define CIRCUIT_PURPOSE_S_HSDIR_POST 20 #define CIRCUIT_PURPOSE_S_HS_MAX_ 20 -/** A testing circuit; not meant to be used for actual traffic. */ +/** A testing circuit; not meant to be used for actual traffic. It is used for + * bandwidth measurement, reachability test and address discovery from an + * authority using the NETINFO cell. */ #define CIRCUIT_PURPOSE_TESTING 21 /** A controller made this circuit and Tor should not use it. */ #define CIRCUIT_PURPOSE_CONTROLLER 22 diff --git a/src/core/or/command.c b/src/core/or/command.c index 9226309ff7..e2bab87def 100644 --- a/src/core/or/command.c +++ b/src/core/or/command.c @@ -331,6 +331,13 @@ command_process_create_cell(cell_t *cell, channel_t *chan) return; } + /* Mark whether this circuit used TAP in case we need to use this + * information for onion service statistics later on. */ + if (create_cell->handshake_type == ONION_HANDSHAKE_TYPE_FAST || + create_cell->handshake_type == ONION_HANDSHAKE_TYPE_TAP) { + circ->used_legacy_circuit_handshake = true; + } + if (!channel_is_client(chan)) { /* remember create types we've seen, but don't remember them from * clients, to be extra conservative about client statistics. */ @@ -587,11 +594,27 @@ command_process_relay_cell(cell_t *cell, channel_t *chan) } /* If this is a cell in an RP circuit, count it as part of the - hidden service stats */ + onion service stats */ if (options->HiddenServiceStatistics && !CIRCUIT_IS_ORIGIN(circ) && - TO_OR_CIRCUIT(circ)->circuit_carries_hs_traffic_stats) { - rep_hist_seen_new_rp_cell(); + CONST_TO_OR_CIRCUIT(circ)->circuit_carries_hs_traffic_stats) { + /** We need to figure out of this is a v2 or v3 RP circuit to count it + * appropriately. v2 services always use the TAP legacy handshake to + * connect to the RP; we use this feature to distinguish between v2/v3. */ + bool is_v2 = false; + if (CONST_TO_OR_CIRCUIT(circ)->used_legacy_circuit_handshake) { + is_v2 = true; + } else if (CONST_TO_OR_CIRCUIT(circ)->rend_splice) { + /* If this is a client->RP circuit we need to check the spliced circuit + * (which is the service->RP circuit) to see if it was using TAP and + * hence if it's a v2 circuit. That's because client->RP circuits can + * still use ntor even on v2; but service->RP will always use TAP. */ + const or_circuit_t *splice = CONST_TO_OR_CIRCUIT(circ)->rend_splice; + if (splice->used_legacy_circuit_handshake) { + is_v2 = true; + } + } + rep_hist_seen_new_rp_cell(is_v2); } } diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 859ad1c6fc..b2390e4f04 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -2209,7 +2209,7 @@ 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 willing to connect there, and and otherwise handle the + * see whether we're willing to connect there, and otherwise handle the * .exit address. * * We'll set chosen_exit_name and/or close the connection as appropriate. diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 70cb19a3f3..40c4441de6 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -566,11 +566,6 @@ connection_or_reached_eof(or_connection_t *conn) int connection_or_process_inbuf(or_connection_t *conn) { - /** Don't let the inbuf of a nonopen OR connection grow beyond this many - * bytes: it's either a broken client, a non-Tor client, or a DOS - * attempt. */ -#define MAX_OR_INBUF_WHEN_NONOPEN 0 - int ret = 0; tor_assert(conn); @@ -581,6 +576,15 @@ connection_or_process_inbuf(or_connection_t *conn) /* start TLS after handshake completion, or deal with error */ if (ret == 1) { tor_assert(TO_CONN(conn)->proxy_state == PROXY_CONNECTED); + if (buf_datalen(conn->base_.inbuf) != 0) { + log_fn(LOG_PROTOCOL_WARN, LD_NET, "Found leftover (%d bytes) " + "when transitioning from PROXY_HANDSHAKING state on %s: " + "closing.", + (int)buf_datalen(conn->base_.inbuf), + connection_describe(TO_CONN(conn))); + connection_or_close_for_error(conn, 0); + return -1; + } if (connection_tls_start_handshake(conn, 0) < 0) ret = -1; /* Touch the channel's active timestamp if there is one */ @@ -601,14 +605,12 @@ connection_or_process_inbuf(or_connection_t *conn) break; /* don't do anything */ } - /* This check was necessary with 0.2.2, when the TLS_SERVER_RENEGOTIATING - * check would otherwise just let data accumulate. It serves no purpose - * in 0.2.3. - * - * XXXX Remove this check once we verify that the above paragraph is - * 100% true. */ - if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) { - log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) " + /* This check makes sure that we don't have any data on the inbuf if we're + * doing our TLS handshake: if we did, they were probably put there by a + * SOCKS proxy trying to trick us into accepting unauthenticated data. + */ + if (buf_datalen(conn->base_.inbuf) != 0) { + log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated data (%d bytes) " "on non-open %s; closing.", (int)buf_datalen(conn->base_.inbuf), connection_describe(TO_CONN(conn))); @@ -684,6 +686,11 @@ connection_or_finished_flushing(or_connection_t *conn) /* PROXY_HAPROXY gets connected by receiving an ack. */ if (conn->proxy_type == PROXY_HAPROXY) { tor_assert(TO_CONN(conn)->proxy_state == PROXY_HAPROXY_WAIT_FOR_FLUSH); + IF_BUG_ONCE(buf_datalen(TO_CONN(conn)->inbuf) != 0) { + /* This should be impossible; we're not even reading. */ + connection_or_close_for_error(conn, 0); + return -1; + } TO_CONN(conn)->proxy_state = PROXY_CONNECTED; if (connection_tls_start_handshake(conn, 0) < 0) { diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h index 4e17b1c143..4da88889ce 100644 --- a/src/core/or/or_circuit_st.h +++ b/src/core/or/or_circuit_st.h @@ -63,6 +63,12 @@ struct or_circuit_t { * statistics. */ unsigned int circuit_carries_hs_traffic_stats : 1; + /** True iff this circuit was made with a CREATE_FAST cell, or a CREATE[2] + * cell with a TAP handshake. If this is the case and this is a rend circuit, + * this is a v2 circuit, otherwise if this is a rend circuit it's a v3 + * circuit. */ + bool used_legacy_circuit_handshake; + /** Number of cells that were removed from circuit queue; reset every * time when writing buffer stats to disk. */ uint32_t processed_cells; diff --git a/src/core/or/sendme.c b/src/core/or/sendme.c index 7b4595c1ac..9cad245b29 100644 --- a/src/core/or/sendme.c +++ b/src/core/or/sendme.c @@ -398,8 +398,8 @@ sendme_connection_edge_consider_sending(edge_connection_t *conn) conn->deliver_window += STREAMWINDOW_INCREMENT; if (connection_edge_send_command(conn, RELAY_COMMAND_SENDME, NULL, 0) < 0) { - log_warn(LD_BUG, "connection_edge_send_command failed while sending " - "a SENDME. Circuit probably closed, skipping."); + log_debug(LD_CIRC, "connection_edge_send_command failed while sending " + "a SENDME. Circuit probably closed, skipping."); goto end; /* The circuit's closed, don't continue */ } } diff --git a/src/core/proto/proto_socks.c b/src/core/proto/proto_socks.c index ada968c6b4..a7ee190b3f 100644 --- a/src/core/proto/proto_socks.c +++ b/src/core/proto/proto_socks.c @@ -550,6 +550,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, if (parsed == -1) { log_warn(LD_APP, "socks5: parsing failed - invalid client request"); res = SOCKS_RESULT_INVALID; + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); goto end; } else if (parsed == -2) { res = SOCKS_RESULT_TRUNCATED; @@ -561,6 +562,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, if (socks5_client_request_get_version(trunnel_req) != 5) { res = SOCKS_RESULT_INVALID; + socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); goto end; } @@ -594,6 +596,7 @@ parse_socks5_client_request(const uint8_t *raw_data, socks_request_t *req, tor_addr_to_str(req->address, &destaddr, sizeof(req->address), 1); } break; default: { + socks_request_set_socks5_error(req, SOCKS5_ADDRESS_TYPE_NOT_SUPPORTED); res = -1; } break; } @@ -774,8 +777,10 @@ handle_socks_message(const uint8_t *raw_data, size_t datalen, } else { res = parse_socks5_client_request(raw_data, req, datalen, drain_out); - if (res != SOCKS_RESULT_DONE) { + if (BUG(res == SOCKS_RESULT_INVALID && req->replylen == 0)) { socks_request_set_socks5_error(req, SOCKS5_GENERAL_ERROR); + } + if (res != SOCKS_RESULT_DONE) { goto end; } diff --git a/src/ext/keccak-tiny/keccak-tiny.h b/src/ext/keccak-tiny/keccak-tiny.h index a9c8ed6420..dd26386a9a 100644 --- a/src/ext/keccak-tiny/keccak-tiny.h +++ b/src/ext/keccak-tiny/keccak-tiny.h @@ -21,7 +21,7 @@ typedef struct keccak_state { size_t offset; uint8_t finalized : 1; -} keccak_state; +} __attribute__((aligned(8))) keccak_state; /* Initialize a Keccak instance suitable for SHA-3 hash functions. */ int keccak_digest_init(keccak_state *s, size_t bits); diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c index 6e10defa13..8e2bb01661 100644 --- a/src/feature/client/bridges.c +++ b/src/feature/client/bridges.c @@ -164,6 +164,17 @@ bridge_get_addr_port(const bridge_info_t *bridge) return &bridge->addrport_configured; } +/** + * Given a <b>bridge</b>, return the transport name. If none were configured, + * NULL is returned. + */ +const char * +bridget_get_transport_name(const bridge_info_t *bridge) +{ + tor_assert(bridge); + return bridge->transport_name; +} + /** If we have a bridge configured whose digest matches <b>digest</b>, or a * bridge with no known digest whose address matches any of the * tor_addr_port_t's in <b>orports</b>, return that bridge. Else return diff --git a/src/feature/client/bridges.h b/src/feature/client/bridges.h index 174149cf97..1b090e8649 100644 --- a/src/feature/client/bridges.h +++ b/src/feature/client/bridges.h @@ -23,6 +23,7 @@ void sweep_bridge_list(void); const smartlist_t *bridge_list_get(void); const uint8_t *bridge_get_rsa_id_digest(const bridge_info_t *bridge); const tor_addr_port_t * bridge_get_addr_port(const bridge_info_t *bridge); +const char *bridget_get_transport_name(const bridge_info_t *bridge); bridge_info_t *get_configured_bridge_by_addr_port_digest( const tor_addr_t *addr, uint16_t port, diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index d1c807189a..4b05d55494 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -368,6 +368,28 @@ static int unconfigured_proxies_n = 0; /** Boolean: True iff we might need to restart some proxies. */ static int check_if_restarts_needed = 0; +/** Return true iff we have a managed_proxy_t in the global list is for the + * given transport name. */ +bool +managed_proxy_has_transport(const char *transport_name) +{ + tor_assert(transport_name); + + if (!managed_proxy_list) { + return false; + } + + SMARTLIST_FOREACH_BEGIN(managed_proxy_list, const managed_proxy_t *, mp) { + SMARTLIST_FOREACH_BEGIN(mp->transports_to_launch, const char *, name) { + if (!strcasecmp(name, transport_name)) { + return true; + } + } SMARTLIST_FOREACH_END(name); + } SMARTLIST_FOREACH_END(mp); + + return false; +} + /** Return true if there are still unconfigured managed proxies, or proxies * that need restarting. */ int diff --git a/src/feature/client/transports.h b/src/feature/client/transports.h index 3aff1cb248..47b118e77b 100644 --- a/src/feature/client/transports.h +++ b/src/feature/client/transports.h @@ -41,6 +41,7 @@ void transport_free_(transport_t *transport); #define transport_free(tr) FREE_AND_NULL(transport_t, transport_free_, (tr)) MOCK_DECL(transport_t*, transport_get_by_name, (const char *name)); +bool managed_proxy_has_transport(const char *transport_name); MOCK_DECL(void, pt_kickstart_proxy, (const smartlist_t *transport_list, char **proxy_argv, diff --git a/src/feature/hs/hs_cache.c b/src/feature/hs/hs_cache.c index 03e004c356..0febcfb40e 100644 --- a/src/feature/hs/hs_cache.c +++ b/src/feature/hs/hs_cache.c @@ -19,6 +19,7 @@ #include "feature/hs/hs_descriptor.h" #include "feature/nodelist/networkstatus.h" #include "feature/rend/rendcache.h" +#include "feature/stats/rephist.h" #include "feature/hs/hs_cache.h" @@ -174,7 +175,10 @@ cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc) * old HS protocol cache subsystem for which we are tied with. */ rend_cache_increment_allocation(cache_get_dir_entry_size(desc)); - /* XXX: Update HS statistics. We should have specific stats for v3. */ + /* Update HSv3 statistics */ + if (get_options()->HiddenServiceStatistics) { + rep_hist_hsdir_stored_maybe_new_v3_onion(desc->key); + } return 0; diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c index c2ea5afe32..2c7d6c8d90 100644 --- a/src/feature/hs_common/shared_random_client.c +++ b/src/feature/hs_common/shared_random_client.c @@ -33,12 +33,11 @@ srv_to_control_string(const sr_srv_t *srv) } /** - * If we have no consensus and we are not an authority, assume that this is - * the voting interval. We should never actually use this: only authorities - * should be trying to figure out the schedule when they don't have a - * consensus. - **/ + * If we have no consensus and we are not an authority, assume that this is the + * voting interval. This can be used while bootstrapping as a relay and we are + * asked to initialize HS stats (see rep_hist_hs_stats_init()) */ #define DEFAULT_NETWORK_VOTING_INTERVAL (3600) +#define TESTING_DEFAULT_NETWORK_VOTING_INTERVAL (20) /* This is an unpleasing workaround for tests. Our unit tests assume that we * are scheduling all of our shared random stuff as if we were a directory @@ -69,11 +68,13 @@ get_voting_interval(void) * It's better than falling back to the non-consensus case. */ interval = (int)(consensus->fresh_until - consensus->valid_after); } else { - /* We should never be reaching this point, since a client should never - * call this code unless they have some kind of a consensus. All we can - * do is hope that this network is using the default voting interval. */ - tor_assert_nonfatal_unreached_once(); - interval = DEFAULT_NETWORK_VOTING_INTERVAL; + /* We can reach this as a relay when bootstrapping and we are asked to + * initialize HS stats (see rep_hist_hs_stats_init()). */ + if (get_options()->TestingTorNetwork) { + interval = TESTING_DEFAULT_NETWORK_VOTING_INTERVAL; + } else { + interval = DEFAULT_NETWORK_VOTING_INTERVAL; + } } tor_assert(interval > 0); return interval; diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index ece3c9e059..5deec01f82 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -240,7 +240,7 @@ networkstatus_get_cache_fname,(int flav, } /** - * Read and and return the cached consensus of type <b>flavorname</b>. If + * Read and return the cached consensus of type <b>flavorname</b>. If * <b>unverified</b> is false, get the one we haven't verified. Return NULL if * the file isn't there. */ static tor_mmap_t * diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 3f6e31bc3a..a1a348edb9 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -65,6 +65,9 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" +#include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "feature/client/bridges.h" #include "feature/control/control_events.h" @@ -137,8 +140,6 @@ static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static const char *signed_descriptor_get_body_impl( const signed_descriptor_t *desc, int with_annotations); -static void launch_dummy_descriptor_download_as_needed(time_t now, - const or_options_t *options); /****************************************************************************/ @@ -2306,7 +2307,6 @@ update_all_descriptor_downloads(time_t now) return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); - launch_dummy_descriptor_download_as_needed(now, get_options()); } /** Clear all our timeouts for fetching v3 directory stuff, and then @@ -2760,54 +2760,6 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, smartlist_free(no_longer_old); } -/** How often should we launch a server/authority request to be sure of getting - * a guess for our IP? */ -/*XXXX+ this info should come from netinfo cells or something, or we should - * do this only when we aren't seeing incoming data. see bug 652. */ -#define DUMMY_DOWNLOAD_INTERVAL (20*60) - -/** As needed, launch a dummy router descriptor fetch to see if our - * address has changed. */ -static void -launch_dummy_descriptor_download_as_needed(time_t now, - const or_options_t *options) -{ - static time_t last_dummy_download = 0; - bool have_addr; - tor_addr_t addr_out; - - /* This dummy fetch only matter for relays. */ - if (!server_mode(options)) { - return; - } - - /* Lookup the address cache to learn if we have a good usable address. We - * still force relays to have an IPv4 so that alone is enough to learn if we - * need a lookup. In case we don't have one, we might want to attempt a - * dummy fetch to learn our address as a suggestion from an authority. */ - have_addr = relay_find_addr_to_publish(options, AF_INET, - RELAY_FIND_ADDR_CACHE_ONLY, - &addr_out); - - /* XXXX+ we could be smarter here; see notes on bug 652. */ - /* If we're a server that doesn't have an address, we rely on directory - * fetches to learn when our address changes. So if we haven't tried to get - * any routerdescs in a long time, try a dummy fetch now. */ - if (!have_addr && - last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now && - last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) { - last_dummy_download = now; - /* XX/teor - do we want an authority here, because they are less likely - * to give us the wrong address? (See #17782) - * I'm leaving the previous behaviour intact, because I don't like - * the idea of some relays contacting an authority every 20 minutes. */ - directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_GENERAL, "authority.z", - PDS_RETRY_IF_NO_SERVERS, - DL_WANT_ANY_DIRSERVER); - } -} - /** Launch downloads for router status as needed. */ void update_router_descriptor_downloads(time_t now) diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c index 1bb8741e45..c45a0b463f 100644 --- a/src/feature/relay/ext_orport.c +++ b/src/feature/relay/ext_orport.c @@ -656,75 +656,17 @@ connection_ext_or_start_auth(or_connection_t *or_conn) return 0; } -/** Global map between Extended ORPort identifiers and OR - * connections. */ -static digestmap_t *orconn_ext_or_id_map = NULL; - -/** Remove the Extended ORPort identifier of <b>conn</b> from the - * global identifier list. Also, clear the identifier from the - * connection itself. */ -void -connection_or_remove_from_ext_or_id_map(or_connection_t *conn) -{ - or_connection_t *tmp; - if (!orconn_ext_or_id_map) - return; - if (!conn->ext_or_conn_id) - return; - - tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id); - if (!tor_digest_is_zero(conn->ext_or_conn_id)) - tor_assert(tmp == conn); - - memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN); -} - -#ifdef TOR_UNIT_TESTS -/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such - * connection is found. */ -or_connection_t * -connection_or_get_by_ext_or_id(const char *id) -{ - if (!orconn_ext_or_id_map) - return NULL; - return digestmap_get(orconn_ext_or_id_map, id); -} -#endif /* defined(TOR_UNIT_TESTS) */ - -/** Deallocate the global Extended ORPort identifier list */ -void -connection_or_clear_ext_or_id_map(void) -{ - digestmap_free(orconn_ext_or_id_map, NULL); - orconn_ext_or_id_map = NULL; -} - /** Creates an Extended ORPort identifier for <b>conn</b> and deposits * it into the global list of identifiers. */ void connection_or_set_ext_or_identifier(or_connection_t *conn) { char random_id[EXT_OR_CONN_ID_LEN]; - or_connection_t *tmp; - - if (!orconn_ext_or_id_map) - orconn_ext_or_id_map = digestmap_new(); - - /* Remove any previous identifiers: */ - if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id)) - connection_or_remove_from_ext_or_id_map(conn); - - do { - crypto_rand(random_id, sizeof(random_id)); - } while (digestmap_get(orconn_ext_or_id_map, random_id)); if (!conn->ext_or_conn_id) conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN); memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN); - - tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn); - tor_assert(!tmp); } /** Free any leftover allocated memory of the ext_orport.c subsystem. */ diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h index 416c358397..b149f9eb1c 100644 --- a/src/feature/relay/ext_orport.h +++ b/src/feature/relay/ext_orport.h @@ -36,8 +36,6 @@ int connection_ext_or_start_auth(or_connection_t *or_conn); void connection_or_set_ext_or_identifier(or_connection_t *conn); -void connection_or_remove_from_ext_or_id_map(or_connection_t *conn); -void connection_or_clear_ext_or_id_map(void); int connection_ext_or_finished_flushing(or_connection_t *conn); int connection_ext_or_process_inbuf(or_connection_t *or_conn); char *get_ext_or_auth_cookie_file_name(void); @@ -71,10 +69,6 @@ connection_ext_or_process_inbuf(or_connection_t *conn) } #define connection_or_set_ext_or_identifier(conn) \ ((void)(conn)) -#define connection_or_remove_from_ext_or_id_map(conn) \ - ((void)(conn)) -#define connection_or_clear_ext_or_id_map() \ - STMT_NIL #define get_ext_or_auth_cookie_file_name() \ (NULL) @@ -94,7 +88,6 @@ STATIC int handle_client_auth_nonce(const char *client_nonce, #ifdef TOR_UNIT_TESTS extern uint8_t *ext_or_auth_cookie; extern int ext_or_auth_cookie_is_set; -or_connection_t *connection_or_get_by_ext_or_id(const char *id); #endif #endif /* defined(EXT_ORPORT_PRIVATE) */ diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index 6504c680eb..e8c29fa7ed 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -227,16 +227,21 @@ remove_duplicate_orports(smartlist_t *ports) if (removing[j]) { continue; } + /* Skip non ORPorts. */ + if (next->type != CONN_TYPE_OR_LISTENER) { + continue; + } /* Same address family and same port number, we have a match. */ - if (!current->explicit_addr && next->explicit_addr && - tor_addr_family(¤t->addr) == tor_addr_family(&next->addr) && + if (tor_addr_family(¤t->addr) == tor_addr_family(&next->addr) && current->port == next->port) { /* Remove current because next is explicitly set. */ removing[i] = true; - char *next_str = tor_strdup(describe_relay_port(next)); - log_warn(LD_CONFIG, "Configuration port %s superseded by %s", - describe_relay_port(current), next_str); - tor_free(next_str); + if (!current->explicit_addr && next->explicit_addr) { + char *next_str = tor_strdup(describe_relay_port(next)); + log_warn(LD_CONFIG, "Configuration port %s superseded by %s", + describe_relay_port(current), next_str); + tor_free(next_str); + } } } } diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c index 43b958d563..9c2c8b281c 100644 --- a/src/feature/relay/relay_find_addr.c +++ b/src/feature/relay/relay_find_addr.c @@ -12,10 +12,16 @@ #include "app/config/resolve_addr.h" #include "core/mainloop/mainloop.h" +#include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "feature/control/control_events.h" #include "feature/dircommon/dir_connection_st.h" #include "feature/nodelist/dirlist.h" +#include "feature/nodelist/node_select.h" +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerstatus_st.h" #include "feature/relay/relay_find_addr.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" @@ -151,3 +157,65 @@ relay_has_address_set(int family) return relay_find_addr_to_publish(get_options(), family, RELAY_FIND_ADDR_CACHE_ONLY, &addr); } + +/** How often should we launch a circuit to an authority to be sure of getting + * a guess for our IP? */ +#define DUMMY_DOWNLOAD_INTERVAL (20*60) + +void +relay_addr_learn_from_dirauth(void) +{ + static time_t last_dummy_circuit = 0; + const or_options_t *options = get_options(); + time_t now = time(NULL); + bool have_addr; + tor_addr_t addr_out; + + /* This dummy circuit only matter for relays. */ + if (BUG(!server_mode(options))) { + return; + } + + /* Lookup the address cache to learn if we have a good usable address. We + * still force relays to have an IPv4 so that alone is enough to learn if we + * need a lookup. In case we don't have one, we might want to attempt a + * dummy circuit to learn our address as a suggestion from an authority. */ + have_addr = relay_find_addr_to_publish(options, AF_INET, + RELAY_FIND_ADDR_CACHE_ONLY, + &addr_out); + + /* If we're a relay or bridge for which we were unable to discover our + * public address, we rely on learning our address from a directory + * authority from the NETINFO cell. */ + if (!have_addr && last_dummy_circuit + DUMMY_DOWNLOAD_INTERVAL < now) { + last_dummy_circuit = now; + + const routerstatus_t *rs = router_pick_trusteddirserver(V3_DIRINFO, 0); + if (BUG(!rs)) { + /* We should really always have trusted directories configured at this + * stage. They are loaded early either from default list or the one + * given in the configuration file. */ + return; + } + const node_t *node = node_get_by_id(rs->identity_digest); + if (BUG(!node)) { + /* If there is a routerstatus_t, there is a node_t thus this should + * never fail. */ + return; + } + extend_info_t *ei = extend_info_from_node(node, 1); + if (BUG(!ei)) { + return; + } + + log_debug(LD_GENERAL, "Attempting dummy testing circuit to an authority " + "in order to learn our address."); + + /* Launch a one-hop testing circuit to a trusted authority so we can learn + * our address through the NETINFO cell. */ + circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, + CIRCLAUNCH_IS_INTERNAL | + CIRCLAUNCH_ONEHOP_TUNNEL); + extend_info_free(ei); + } +} diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h index 3d30946b05..34890cd34e 100644 --- a/src/feature/relay/relay_find_addr.h +++ b/src/feature/relay/relay_find_addr.h @@ -24,6 +24,8 @@ MOCK_DECL(bool, relay_find_addr_to_publish, bool relay_has_address_set(int family); +void relay_addr_learn_from_dirauth(void); + #ifdef RELAY_FIND_ADDR_PRIVATE #endif /* RELAY_FIND_ADDR_PRIVATE */ diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 29103ed6c6..04ffe8e48e 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -831,6 +831,25 @@ router_initialize_tls_context(void) (unsigned int)lifetime); } +/** Announce URL to bridge status page. */ +STATIC void +router_announce_bridge_status_page(void) +{ + char fingerprint[FINGERPRINT_LEN + 1]; + + if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), + fingerprint) < 0) { + // LCOV_EXCL_START + log_err(LD_GENERAL, "Unable to compute bridge fingerprint"); + return; + // LCOV_EXCL_STOP + } + + log_notice(LD_GENERAL, "You can check the status of your bridge relay at " + "https://bridges.torproject.org/status?id=%s", + fingerprint); +} + /** Compute fingerprint (or hashed fingerprint if hashed is 1) and write * it to 'fingerprint' (or 'hashed-fingerprint'). Return 0 on success, or * -1 if Tor should die, @@ -1133,6 +1152,10 @@ init_keys(void) return -1; } + /* Display URL to bridge status page. */ + if (! public_server_mode(options)) + router_announce_bridge_status_page(); + if (!authdir_mode(options)) return 0; /* 6. [authdirserver only] load approved-routers file */ @@ -2071,7 +2094,9 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) /* Tor requires a relay to have an IPv4 so bail if we can't find it. */ if (!have_v4) { - log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); + log_info(LD_CONFIG, "Don't know my address while generating descriptor. " + "Launching circuit to authority to learn it."); + relay_addr_learn_from_dirauth(); result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR; goto err; } @@ -2679,8 +2704,21 @@ check_descriptor_ipaddress_changed(time_t now) /* Ignore returned value because we want to notice not only an address * change but also if an address is lost (current == UNSPEC). */ - find_my_address(get_options(), family, LOG_INFO, ¤t, &method, - &hostname); + bool found = find_my_address(get_options(), family, LOG_INFO, ¤t, + &method, &hostname); + if (!found) { + /* Address was possibly not found because it is simply not configured or + * discoverable. Fallback to our cache, which includes any suggestion + * sent by a trusted directory server. */ + found = relay_find_addr_to_publish(get_options(), family, + RELAY_FIND_ADDR_CACHE_ONLY, + ¤t); + } + + /* The "current" address might be UNSPEC meaning it was not discovered nor + * found in our current cache. If we had an address before and we have + * none now, we consider this an IP change since it appears the relay lost + * its address. */ if (!tor_addr_eq(previous, ¤t)) { char *source; @@ -3291,6 +3329,11 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, "hidserv-stats-end", now, &contents) > 0) { smartlist_add(chunks, contents); } + if (options->HiddenServiceStatistics && + load_stats_file("stats"PATH_SEPARATOR"hidserv-v3-stats", + "hidserv-v3-stats-end", now, &contents) > 0) { + smartlist_add(chunks, contents); + } if (options->EntryStatistics && load_stats_file("stats"PATH_SEPARATOR"entry-stats", "entry-stats-end", now, &contents) > 0) { diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index f71ada8eb7..f9df1f5637 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -129,6 +129,7 @@ void router_free_all(void); STATIC void get_platform_str(char *platform, size_t len); STATIC int router_write_fingerprint(int hashed, int ed25519_identity); STATIC smartlist_t *get_my_declared_family(const or_options_t *options); +STATIC void router_announce_bridge_status_page(void); #ifdef TOR_UNIT_TESTS extern time_t desc_clean_since; diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index b711c94eb6..86b1533be1 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -274,7 +274,12 @@ router_do_orport_reachability_checks(const routerinfo_t *me, !orport_reachable ? "reachability" : "bandwidth", family_name, fmt_addrport_ap(ap)); - inform_testing_reachability(&ap->addr, ap->port, false); + if (!orport_reachable) { + /* Only log if we are actually doing a reachability test to learn if our + * ORPort is reachable. Else, this prints a log notice if we are simply + * opening a bandwidth testing circuit even do we are reachable. */ + inform_testing_reachability(&ap->addr, ap->port, false); + } circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, CIRCLAUNCH_NEED_CAPACITY| diff --git a/src/feature/rend/rendcache.c b/src/feature/rend/rendcache.c index 04f6390a7f..a471c8f463 100644 --- a/src/feature/rend/rendcache.c +++ b/src/feature/rend/rendcache.c @@ -718,7 +718,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc) safe_str(desc_id_base32), (int)encoded_size); /* Statistics: Note down this potentially new HS. */ if (options->HiddenServiceStatistics) { - rep_hist_stored_maybe_new_hs(e->parsed->pk); + rep_hist_hsdir_stored_maybe_new_v2_onion(e->parsed->pk); } number_stored++; diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index 3c22fda3b8..f8d7887e65 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -1710,123 +1710,248 @@ rep_hist_log_circuit_handshake_stats(time_t now) /** Start of the current hidden service stats interval or 0 if we're * not collecting hidden service statistics. */ -static time_t start_of_hs_stats_interval; +static time_t start_of_hs_v2_stats_interval; -/** Carries the various hidden service statistics, and any other - * information needed. */ -typedef struct hs_stats_t { - /** How many relay cells have we seen as rendezvous points? */ - uint64_t rp_relay_cells_seen; +/** Our v2 statistics structure singleton. */ +static hs_v2_stats_t *hs_v2_stats = NULL; - /** Set of unique public key digests we've seen this stat period - * (could also be implemented as sorted smartlist). */ - digestmap_t *onions_seen_this_period; -} hs_stats_t; +/** HSv2 stats */ -/** Our statistics structure singleton. */ -static hs_stats_t *hs_stats = NULL; - -/** Allocate, initialize and return an hs_stats_t structure. */ -static hs_stats_t * -hs_stats_new(void) +/** Allocate, initialize and return an hs_v2_stats_t structure. */ +static hs_v2_stats_t * +hs_v2_stats_new(void) { - hs_stats_t *new_hs_stats = tor_malloc_zero(sizeof(hs_stats_t)); - new_hs_stats->onions_seen_this_period = digestmap_new(); + hs_v2_stats_t *new_hs_v2_stats = tor_malloc_zero(sizeof(hs_v2_stats_t)); + new_hs_v2_stats->v2_onions_seen_this_period = digestmap_new(); - return new_hs_stats; + return new_hs_v2_stats; } -#define hs_stats_free(val) \ - FREE_AND_NULL(hs_stats_t, hs_stats_free_, (val)) +#define hs_v2_stats_free(val) \ + FREE_AND_NULL(hs_v2_stats_t, hs_v2_stats_free_, (val)) -/** Free an hs_stats_t structure. */ +/** Free an hs_v2_stats_t structure. */ static void -hs_stats_free_(hs_stats_t *victim_hs_stats) +hs_v2_stats_free_(hs_v2_stats_t *victim_hs_v2_stats) { - if (!victim_hs_stats) { + if (!victim_hs_v2_stats) { return; } - digestmap_free(victim_hs_stats->onions_seen_this_period, NULL); - tor_free(victim_hs_stats); + digestmap_free(victim_hs_v2_stats->v2_onions_seen_this_period, NULL); + tor_free(victim_hs_v2_stats); } -/** Initialize hidden service statistics. */ +/** Clear history of hidden service statistics and set the measurement + * interval start to <b>now</b>. */ +static void +rep_hist_reset_hs_v2_stats(time_t now) +{ + if (!hs_v2_stats) { + hs_v2_stats = hs_v2_stats_new(); + } + + hs_v2_stats->rp_v2_relay_cells_seen = 0; + + digestmap_free(hs_v2_stats->v2_onions_seen_this_period, NULL); + hs_v2_stats->v2_onions_seen_this_period = digestmap_new(); + + start_of_hs_v2_stats_interval = now; +} + +/** As HSDirs, we saw another v2 onion with public key <b>pubkey</b>. Check + * whether we have counted it before, if not count it now! */ void -rep_hist_hs_stats_init(time_t now) +rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey) +{ + char pubkey_hash[DIGEST_LEN]; + + if (!hs_v2_stats) { + return; // We're not collecting stats + } + + /* Get the digest of the pubkey which will be used to detect whether + we've seen this hidden service before or not. */ + if (crypto_pk_get_digest(pubkey, pubkey_hash) < 0) { + /* This fail should not happen; key has been validated by + descriptor parsing code first. */ + return; + } + + /* Check if this is the first time we've seen this hidden + service. If it is, count it as new. */ + if (!digestmap_get(hs_v2_stats->v2_onions_seen_this_period, + pubkey_hash)) { + digestmap_set(hs_v2_stats->v2_onions_seen_this_period, + pubkey_hash, (void*)(uintptr_t)1); + } +} + +/*** HSv3 stats ******/ + +/** Start of the current hidden service stats interval or 0 if we're not + * collecting hidden service statistics. + * + * This is particularly important for v3 statistics since this variable + * controls the start time of initial v3 stats collection. It's initialized by + * rep_hist_hs_stats_init() to the next time period start (i.e. 12:00UTC), and + * should_collect_v3_stats() ensures that functions that collect v3 stats do + * not do so sooner than that. + * + * Collecting stats from 12:00UTC to 12:00UTC is extremely important for v3 + * stats because rep_hist_hsdir_stored_maybe_new_v3_onion() uses the blinded + * key of each onion service as its double-counting index. Onion services + * rotate their descriptor at around 00:00UTC which means that their blinded + * key also changes around that time. However the precise time that onion + * services rotate their descriptors is actually when they fetch a new + * 00:00UTC consensus and that happens at a random time (e.g. it can even + * happen at 02:00UTC). This means that if we started keeping v3 stats at + * around 00:00UTC we wouldn't be able to tell when onion services change + * their blinded key and hence we would double count an unpredictable amount + * of them (for example, if an onion service fetches the 00:00UTC consensus at + * 01:00UTC it would upload to its old HSDir at 00:45UTC, and then to a + * different HSDir at 01:50UTC). + * + * For this reason, we start collecting statistics at 12:00UTC. This way we + * know that by the time we stop collecting statistics for that time period 24 + * hours later, all the onion services have switched to their new blinded + * key. This way we can predict much better how much double counting has been + * performed. + */ +static time_t start_of_hs_v3_stats_interval; + +/** Our v3 statistics structure singleton. */ +static hs_v3_stats_t *hs_v3_stats = NULL; + +/** Allocate, initialize and return an hs_v3_stats_t structure. */ +static hs_v3_stats_t * +hs_v3_stats_new(void) +{ + hs_v3_stats_t *new_hs_v3_stats = tor_malloc_zero(sizeof(hs_v3_stats_t)); + new_hs_v3_stats->v3_onions_seen_this_period = digest256map_new(); + + return new_hs_v3_stats; +} + +#define hs_v3_stats_free(val) \ + FREE_AND_NULL(hs_v3_stats_t, hs_v3_stats_free_, (val)) + +/** Free an hs_v3_stats_t structure. */ +static void +hs_v3_stats_free_(hs_v3_stats_t *victim_hs_v3_stats) { - if (!hs_stats) { - hs_stats = hs_stats_new(); + if (!victim_hs_v3_stats) { + return; } - start_of_hs_stats_interval = now; + digest256map_free(victim_hs_v3_stats->v3_onions_seen_this_period, NULL); + tor_free(victim_hs_v3_stats); } /** Clear history of hidden service statistics and set the measurement * interval start to <b>now</b>. */ static void -rep_hist_reset_hs_stats(time_t now) +rep_hist_reset_hs_v3_stats(time_t now) { - if (!hs_stats) { - hs_stats = hs_stats_new(); + if (!hs_v3_stats) { + hs_v3_stats = hs_v3_stats_new(); } - hs_stats->rp_relay_cells_seen = 0; + digest256map_free(hs_v3_stats->v3_onions_seen_this_period, NULL); + hs_v3_stats->v3_onions_seen_this_period = digest256map_new(); - digestmap_free(hs_stats->onions_seen_this_period, NULL); - hs_stats->onions_seen_this_period = digestmap_new(); + hs_v3_stats->rp_v3_relay_cells_seen = 0; - start_of_hs_stats_interval = now; + start_of_hs_v3_stats_interval = now; } -/** Stop collecting hidden service stats in a way that we can re-start - * doing so in rep_hist_buffer_stats_init(). */ -void -rep_hist_hs_stats_term(void) +/** Return true if it's a good time to collect v3 stats. + * + * v3 stats have a strict stats collection period (from 12:00UTC to 12:00UTC + * on the real network). We don't want to collect statistics if (for example) + * we just booted and it's 03:00UTC; we will wait until 12:00UTC before we + * start collecting statistics to make sure that the final result represents + * the whole collection period. This behavior is controlled by + * rep_hist_hs_stats_init(). + */ +MOCK_IMPL(STATIC bool, +should_collect_v3_stats,(void)) { - rep_hist_reset_hs_stats(0); + return start_of_hs_v3_stats_interval <= approx_time(); } -/** We saw a new HS relay cell, Count it! */ +/** We just received a new descriptor with <b>blinded_key</b>. See if we've + * seen this blinded key before, and if not add it to the stats. */ void -rep_hist_seen_new_rp_cell(void) +rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key) { - if (!hs_stats) { - return; // We're not collecting stats + /* Return early if we don't collect HSv3 stats, or if it's not yet the time + * to collect them. */ + if (!hs_v3_stats || !should_collect_v3_stats()) { + return; } - hs_stats->rp_relay_cells_seen++; + bool seen_before = + !!digest256map_get(hs_v3_stats->v3_onions_seen_this_period, + blinded_key); + + log_info(LD_GENERAL, "Considering v3 descriptor with %s (%sseen before)", + safe_str(hex_str((char*)blinded_key, 32)), + seen_before ? "" : "not "); + + /* Count it if we haven't seen it before. */ + if (!seen_before) { + digest256map_set(hs_v3_stats->v3_onions_seen_this_period, + blinded_key, (void*)(uintptr_t)1); + } } -/** As HSDirs, we saw another hidden service with public key - * <b>pubkey</b>. Check whether we have counted it before, if not - * count it now! */ +/** We saw a new HS relay cell: count it! + * If <b>is_v2</b> is set then it's a v2 RP cell, otherwise it's a v3. */ void -rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey) +rep_hist_seen_new_rp_cell(bool is_v2) { - char pubkey_hash[DIGEST_LEN]; + log_debug(LD_GENERAL, "New RP cell (%d)", is_v2); - if (!hs_stats) { - return; // We're not collecting stats + if (is_v2 && hs_v2_stats) { + hs_v2_stats->rp_v2_relay_cells_seen++; + } else if (!is_v2 && hs_v3_stats && should_collect_v3_stats()) { + hs_v3_stats->rp_v3_relay_cells_seen++; } +} - /* Get the digest of the pubkey which will be used to detect whether - we've seen this hidden service before or not. */ - if (crypto_pk_get_digest(pubkey, pubkey_hash) < 0) { - /* This fail should not happen; key has been validated by - descriptor parsing code first. */ - return; +/** Generic HS stats code */ + +/** Initialize v2 and v3 hidden service statistics. */ +void +rep_hist_hs_stats_init(time_t now) +{ + if (!hs_v2_stats) { + hs_v2_stats = hs_v2_stats_new(); } - /* Check if this is the first time we've seen this hidden - service. If it is, count it as new. */ - if (!digestmap_get(hs_stats->onions_seen_this_period, - pubkey_hash)) { - digestmap_set(hs_stats->onions_seen_this_period, - pubkey_hash, (void*)(uintptr_t)1); + /* Start collecting v2 stats straight away */ + start_of_hs_v2_stats_interval = now; + + if (!hs_v3_stats) { + hs_v3_stats = hs_v3_stats_new(); } + + /* Start collecting v3 stats at the next 12:00 UTC */ + start_of_hs_v3_stats_interval = hs_get_start_time_of_next_time_period(now); +} + +/** Stop collecting hidden service stats in a way that we can re-start + * doing so in rep_hist_buffer_stats_init(). */ +void +rep_hist_hs_stats_term(void) +{ + rep_hist_reset_hs_v2_stats(0); + rep_hist_reset_hs_v3_stats(0); } +/** Stats reporting code */ + /* The number of cells that are supposed to be hidden from the adversary * by adding noise from the Laplace distribution. This value, divided by * EPSILON, is Laplace parameter b. It must be greater than 0. */ @@ -1851,58 +1976,69 @@ rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey) #define ONIONS_SEEN_BIN_SIZE 8 /** Allocate and return a string containing hidden service stats that - * are meant to be placed in the extra-info descriptor. */ -static char * -rep_hist_format_hs_stats(time_t now) + * are meant to be placed in the extra-info descriptor. + * + * Function works for both v2 and v3 stats depending on <b>is_v3</b>. */ +STATIC char * +rep_hist_format_hs_stats(time_t now, bool is_v3) { char t[ISO_TIME_LEN+1]; char *hs_stats_string; - int64_t obfuscated_cells_seen; - int64_t obfuscated_onions_seen; + int64_t obfuscated_onions_seen, obfuscated_cells_seen; + + uint64_t rp_cells_seen = is_v3 ? + hs_v3_stats->rp_v3_relay_cells_seen : hs_v2_stats->rp_v2_relay_cells_seen; + size_t onions_seen = is_v3 ? + digest256map_size(hs_v3_stats->v3_onions_seen_this_period) : + digestmap_size(hs_v2_stats->v2_onions_seen_this_period); + time_t start_of_hs_stats_interval = is_v3 ? + start_of_hs_v3_stats_interval : start_of_hs_v2_stats_interval; uint64_t rounded_cells_seen - = round_uint64_to_next_multiple_of(hs_stats->rp_relay_cells_seen, - REND_CELLS_BIN_SIZE); + = round_uint64_to_next_multiple_of(rp_cells_seen, REND_CELLS_BIN_SIZE); rounded_cells_seen = MIN(rounded_cells_seen, INT64_MAX); obfuscated_cells_seen = add_laplace_noise((int64_t)rounded_cells_seen, crypto_rand_double(), REND_CELLS_DELTA_F, REND_CELLS_EPSILON); uint64_t rounded_onions_seen = - round_uint64_to_next_multiple_of((size_t)digestmap_size( - hs_stats->onions_seen_this_period), - ONIONS_SEEN_BIN_SIZE); + round_uint64_to_next_multiple_of(onions_seen, ONIONS_SEEN_BIN_SIZE); rounded_onions_seen = MIN(rounded_onions_seen, INT64_MAX); obfuscated_onions_seen = add_laplace_noise((int64_t)rounded_onions_seen, crypto_rand_double(), ONIONS_SEEN_DELTA_F, ONIONS_SEEN_EPSILON); format_iso_time(t, now); - tor_asprintf(&hs_stats_string, "hidserv-stats-end %s (%d s)\n" - "hidserv-rend-relayed-cells %"PRId64" delta_f=%d " - "epsilon=%.2f bin_size=%d\n" - "hidserv-dir-onions-seen %"PRId64" delta_f=%d " - "epsilon=%.2f bin_size=%d\n", + tor_asprintf(&hs_stats_string, "%s %s (%u s)\n" + "%s %"PRId64" delta_f=%d epsilon=%.2f bin_size=%d\n" + "%s %"PRId64" delta_f=%d epsilon=%.2f bin_size=%d\n", + is_v3 ? "hidserv-v3-stats-end" : "hidserv-stats-end", t, (unsigned) (now - start_of_hs_stats_interval), - (obfuscated_cells_seen), REND_CELLS_DELTA_F, + is_v3 ? + "hidserv-rend-v3-relayed-cells" : "hidserv-rend-relayed-cells", + obfuscated_cells_seen, REND_CELLS_DELTA_F, REND_CELLS_EPSILON, REND_CELLS_BIN_SIZE, - (obfuscated_onions_seen), - ONIONS_SEEN_DELTA_F, + is_v3 ? "hidserv-dir-v3-onions-seen" :"hidserv-dir-onions-seen", + obfuscated_onions_seen, ONIONS_SEEN_DELTA_F, ONIONS_SEEN_EPSILON, ONIONS_SEEN_BIN_SIZE); return hs_stats_string; } /** If 24 hours have passed since the beginning of the current HS - * stats period, write buffer stats to $DATADIR/stats/hidserv-stats + * stats period, write buffer stats to $DATADIR/stats/hidserv-v3-stats * (possibly overwriting an existing file) and reset counters. Return * when we would next want to write buffer stats or 0 if we never want to - * write. */ + * write. Function works for both v2 and v3 stats depending on <b>is_v3</b>. + */ time_t -rep_hist_hs_stats_write(time_t now) +rep_hist_hs_stats_write(time_t now, bool is_v3) { char *str = NULL; + time_t start_of_hs_stats_interval = is_v3 ? + start_of_hs_v3_stats_interval : start_of_hs_v2_stats_interval; + if (!start_of_hs_stats_interval) { return 0; /* Not initialized. */ } @@ -1912,15 +2048,20 @@ rep_hist_hs_stats_write(time_t now) } /* Generate history string. */ - str = rep_hist_format_hs_stats(now); + str = rep_hist_format_hs_stats(now, is_v3); /* Reset HS history. */ - rep_hist_reset_hs_stats(now); + if (is_v3) { + rep_hist_reset_hs_v3_stats(now); + } else { + rep_hist_reset_hs_v2_stats(now); + } /* Try to write to disk. */ if (!check_or_create_data_subdir("stats")) { - write_to_data_subdir("stats", "hidserv-stats", str, - "hidden service stats"); + write_to_data_subdir("stats", + is_v3 ? "hidserv-v3-stats" : "hidserv-stats", + str, "hidden service stats"); } done: @@ -2134,7 +2275,8 @@ rep_hist_log_link_protocol_counts(void) void rep_hist_free_all(void) { - hs_stats_free(hs_stats); + hs_v2_stats_free(hs_v2_stats); + hs_v3_stats_free(hs_v3_stats); digestmap_free(history_map, free_or_history); tor_free(exit_bytes_read); @@ -2155,3 +2297,19 @@ rep_hist_free_all(void) tor_assert_nonfatal(rephist_total_alloc == 0); tor_assert_nonfatal_once(rephist_total_num == 0); } + +#ifdef TOR_UNIT_TESTS +/* only exists for unit tests: get HSv2 stats object */ +const hs_v2_stats_t * +rep_hist_get_hs_v2_stats(void) +{ + return hs_v2_stats; +} + +/* only exists for unit tests: get HSv2 stats object */ +const hs_v3_stats_t * +rep_hist_get_hs_v3_stats(void) +{ + return hs_v3_stats; +} +#endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index c9ebc5c328..de27b16ae0 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -65,10 +65,14 @@ MOCK_DECL(int, rep_hist_get_circuit_handshake_assigned, (uint16_t type)); void rep_hist_hs_stats_init(time_t now); void rep_hist_hs_stats_term(void); -time_t rep_hist_hs_stats_write(time_t now); -char *rep_hist_get_hs_stats_string(void); -void rep_hist_seen_new_rp_cell(void); -void rep_hist_stored_maybe_new_hs(const crypto_pk_t *pubkey); +time_t rep_hist_hs_stats_write(time_t now, bool is_v3); + +char *rep_hist_get_hs_v2_stats_string(void); +void rep_hist_seen_new_rp_cell(bool is_v2); +void rep_hist_hsdir_stored_maybe_new_v2_onion(const crypto_pk_t *pubkey); + +char *rep_hist_get_hs_v3_stats_string(void); +void rep_hist_hsdir_stored_maybe_new_v3_onion(const uint8_t *blinded_key); void rep_hist_free_all(void); @@ -83,6 +87,40 @@ extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1]; extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1]; #endif +#ifdef REPHIST_PRIVATE +/** Carries the various hidden service statistics, and any other + * information needed. */ +typedef struct hs_v2_stats_t { + /** How many v2 relay cells have we seen as rendezvous points? */ + uint64_t rp_v2_relay_cells_seen; + + /** Set of unique public key digests we've seen this stat period + * (could also be implemented as sorted smartlist). */ + digestmap_t *v2_onions_seen_this_period; +} hs_v2_stats_t; + +/** Structure that contains the various statistics we keep about v3 + * services. + * + * Because of the time period logic of v3 services, v3 statistics are more + * sensitive to time than v2 stats. For this reason, we collect v3 + * statistics strictly from 12:00UTC to 12:00UTC as dictated by + * 'start_of_hs_v3_stats_interval'. + **/ +typedef struct hs_v3_stats_t { + /** How many v3 relay cells have we seen as a rendezvous point? */ + uint64_t rp_v3_relay_cells_seen; + + /* The number of unique v3 onion descriptors (actually, unique v3 blind keys) + * we've seen during the measurement period */ + digest256map_t *v3_onions_seen_this_period; +} hs_v3_stats_t; + +MOCK_DECL(STATIC bool, should_collect_v3_stats,(void)); + +STATIC char *rep_hist_format_hs_stats(time_t now, bool is_v3); +#endif /* defined(REPHIST_PRIVATE) */ + /** * Represents the type of a cell for padding accounting */ @@ -108,4 +146,11 @@ void rep_hist_reset_padding_counts(void); void rep_hist_prep_published_padding_counts(time_t now); void rep_hist_padding_count_timers(uint64_t num_timers); +#ifdef TOR_UNIT_TESTS +struct hs_v2_stats_t; +const struct hs_v2_stats_t *rep_hist_get_hs_v2_stats(void); +struct hs_v3_stats_t; +const struct hs_v3_stats_t *rep_hist_get_hs_v3_stats(void); +#endif + #endif /* !defined(TOR_REPHIST_H) */ diff --git a/src/lib/crypt_ops/compat_openssl.h b/src/lib/crypt_ops/compat_openssl.h index c2e1459078..aa66e0c3fa 100644 --- a/src/lib/crypt_ops/compat_openssl.h +++ b/src/lib/crypt_ops/compat_openssl.h @@ -34,7 +34,7 @@ #ifndef OPENSSL_1_1_API #define OpenSSL_version(v) SSLeay_version(v) -#define OpenSSL_version_num() SSLeay() +#define tor_OpenSSL_version_num() SSLeay() #define RAND_OpenSSL() RAND_SSLeay() #define STATE_IS_SW_SERVER_HELLO(st) \ (((st) == SSL3_ST_SW_SRVR_HELLO_A) || \ @@ -42,6 +42,7 @@ #define OSSL_HANDSHAKE_STATE int #define CONST_IF_OPENSSL_1_1_API #else /* defined(OPENSSL_1_1_API) */ +#define tor_OpenSSL_version_num() OpenSSL_version_num() #define STATE_IS_SW_SERVER_HELLO(st) \ ((st) == TLS_ST_SW_SRVR_HELLO) #define CONST_IF_OPENSSL_1_1_API const diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index 065cbca1cc..e763491a11 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/src/lib/crypt_ops/crypto_openssl_mgt.c @@ -222,7 +222,7 @@ crypto_openssl_early_init(void) setup_openssl_threading(); - unsigned long version_num = OpenSSL_version_num(); + unsigned long version_num = tor_OpenSSL_version_num(); const char *version_str = crypto_openssl_get_version_str(); if (version_num == OPENSSL_VERSION_NUMBER && !strcmp(version_str, OPENSSL_VERSION_TEXT)) { diff --git a/src/lib/fs/conffile.c b/src/lib/fs/conffile.c index f1f6d8ae5f..0d0bdf09a6 100644 --- a/src/lib/fs/conffile.c +++ b/src/lib/fs/conffile.c @@ -19,9 +19,11 @@ #include "lib/fs/path.h" #include "lib/log/log.h" #include "lib/malloc/malloc.h" +#include "lib/sandbox/sandbox.h" #include "lib/string/printf.h" #include <stdbool.h> +#include <errno.h> static smartlist_t *config_get_file_list(const char *path, smartlist_t *opened_files); @@ -52,21 +54,26 @@ config_get_lines_include(const char *string, config_line_t **result, opened_lst, 1, NULL, config_process_include); } -/** Returns a list of paths obtained when expading globs in <b>pattern</b>. If - * <b>pattern</b> has no globs, returns a list with <b>pattern</b> if it is an - * existing path or NULL otherwise. If <b>opened_files</b> is provided, adds - * paths opened by glob to it. Returns NULL on failure. */ +/** Return a list of paths obtained when expading globs in <b>pattern</b>. + * If <b>pattern</b> has no globs, return a list with <b>pattern</b> in it. + * If <b>opened_files</b> is provided, add paths opened by glob to it. + * Return NULL on failure. */ static smartlist_t * expand_glob(const char *pattern, smartlist_t *opened_files) { - smartlist_t *matches = tor_glob(pattern); - if (!matches) { - return NULL; + if (! has_glob(pattern)) { + smartlist_t *matches = smartlist_new(); + smartlist_add_strdup(matches, pattern); + return matches; } - // if it is not a glob, return error when the path is missing - if (!has_glob(pattern) && smartlist_len(matches) == 0) { - smartlist_free(matches); + smartlist_t *matches = tor_glob(pattern); + if (!matches) { + if (errno == EPERM) { + log_err(LD_CONFIG, "Sandbox is active, but the configuration pattern " + "\"%s\" listed with %%include would access files or folders not " + "allowed by it. Cannot proceed.", pattern); + } return NULL; } @@ -107,6 +114,13 @@ config_get_file_list(const char *pattern, smartlist_t *opened_files) if (opened_files) { smartlist_add_strdup(opened_files, path); } + if (sandbox_interned_string_is_missing(path)) { + log_err(LD_CONFIG, "Sandbox is active, but a new configuration " + "file \"%s\" has been listed with %%include. Cannot proceed.", + path); + error_found = true; + break; + } file_status_t file_type = file_status(path); if (file_type == FN_FILE) { @@ -201,6 +215,13 @@ config_process_include(const char *pattern, int recursion_level, int extended, int rv = -1; SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) { + if (sandbox_interned_string_is_missing(config_file)) { + log_err(LD_CONFIG, "Sandbox is active, but a new configuration " + "file \"%s\" has been listed with %%include. Cannot proceed.", + config_file); + goto done; + } + log_notice(LD_CONFIG, "Including configuration file \"%s\".", config_file); config_line_t *included_config = NULL; config_line_t *included_config_last = NULL; diff --git a/src/lib/fs/path.c b/src/lib/fs/path.c index fc759f6169..c2fdddb9db 100644 --- a/src/lib/fs/path.c +++ b/src/lib/fs/path.c @@ -537,6 +537,10 @@ unglob_win32(const char *pattern, int prev_sep, int next_sep) static DIR * prot_opendir(const char *name) { + if (sandbox_interned_string_is_missing(name)) { + errno = EPERM; + return NULL; + } return opendir(sandbox_intern_string(name)); } @@ -544,6 +548,10 @@ prot_opendir(const char *name) static int prot_stat(const char *pathname, struct stat *buf) { + if (sandbox_interned_string_is_missing(pathname)) { + errno = EPERM; + return -1; + } return stat(sandbox_intern_string(pathname), buf); } @@ -551,6 +559,10 @@ prot_stat(const char *pathname, struct stat *buf) static int prot_lstat(const char *pathname, struct stat *buf) { + if (sandbox_interned_string_is_missing(pathname)) { + errno = EPERM; + return -1; + } return lstat(sandbox_intern_string(pathname), buf); } /** As closedir, but has the right type for gl_closedir */ @@ -563,7 +575,8 @@ wrap_closedir(void *arg) /** Return a new list containing the paths that match the pattern * <b>pattern</b>. Return NULL on error. On POSIX systems, errno is set by the - * glob function. + * glob function or is set to EPERM if glob tried to access a file not allowed + * by the seccomp sandbox. */ struct smartlist_t * tor_glob(const char *pattern) diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c index 8d467c516e..168dfd943c 100644 --- a/src/lib/sandbox/sandbox.c +++ b/src/lib/sandbox/sandbox.c @@ -310,6 +310,8 @@ static int filter_nopar_gen[] = { #define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \ seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4)) +static const char *sandbox_get_interned_string(const char *str); + /** * Function responsible for setting up the rt_sigaction syscall for * the seccomp filter sandbox. @@ -1224,9 +1226,42 @@ static sandbox_filter_func_t filter_func[] = { sb_kill }; +/** + * Return the interned (and hopefully sandbox-permitted) string equal + * to @a str. + * + * Return NULL if `str` is NULL, or `str` is not an interned string. + **/ const char * sandbox_intern_string(const char *str) { + const char *interned = sandbox_get_interned_string(str); + + if (sandbox_active && str != NULL && interned == NULL) { + log_warn(LD_BUG, "No interned sandbox parameter found for %s", str); + } + + return interned ? interned : str; +} + +/** + * Return true if the sandbox is running and we are missing an interned string + * equal to @a str. + */ +bool +sandbox_interned_string_is_missing(const char *str) +{ + return sandbox_active && sandbox_get_interned_string(str) == NULL; +} + +/** + * Try to find and return the interned string equal to @a str. + * + * If there is no such string, return NULL. + **/ +static const char * +sandbox_get_interned_string(const char *str) +{ sandbox_cfg_t *elem; if (str == NULL) @@ -1245,9 +1280,7 @@ sandbox_intern_string(const char *str) } } - if (sandbox_active) - log_warn(LD_BUG, "No interned sandbox parameter found for %s", str); - return str; + return NULL; } /* DOCDOC */ diff --git a/src/lib/sandbox/sandbox.h b/src/lib/sandbox/sandbox.h index a2b3227b90..eba99afbde 100644 --- a/src/lib/sandbox/sandbox.h +++ b/src/lib/sandbox/sandbox.h @@ -104,12 +104,11 @@ typedef struct { #endif /* defined(USE_LIBSECCOMP) */ #ifdef USE_LIBSECCOMP -/** Returns a registered protected string used with the sandbox, given that - * it matches the parameter. - */ const char* sandbox_intern_string(const char *param); +bool sandbox_interned_string_is_missing(const char *s); #else /* !defined(USE_LIBSECCOMP) */ #define sandbox_intern_string(s) (s) +#define sandbox_interned_string_is_missing(s) (false) #endif /* defined(USE_LIBSECCOMP) */ /** Creates an empty sandbox configuration file.*/ diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 2269714141..ad9b49ab4f 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -342,7 +342,7 @@ tor_tls_init(void) #if (SIZEOF_VOID_P >= 8 && \ OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1)) - long version = OpenSSL_version_num(); + long version = tor_OpenSSL_version_num(); /* LCOV_EXCL_START : we can't test these lines on the same machine */ if (version >= OPENSSL_V_SERIES(1,0,1)) { diff --git a/src/test/hs_build_address.py b/src/test/hs_build_address.py index 91864eabcb..216b7626bf 100644 --- a/src/test/hs_build_address.py +++ b/src/test/hs_build_address.py @@ -10,17 +10,21 @@ import base64 # Python 3.6+, the SHA3 is available in hashlib natively. Else this requires # the pysha3 package (pip install pysha3). +TEST_INPUT = b"Hello World" if sys.version_info < (3, 6): import sha3 + m = sha3.sha3_256(TEST_INPUT) +else: + m = hashlib.sha3_256(TEST_INPUT) # Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 # used the old Keccak implementation. During the finalization of SHA3, NIST # changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function # stayed the same. pysha3 1.0 provides the previous Keccak hash, too. TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" -if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): +if TEST_VALUE != m.hexdigest(): print("pysha3 version is < 1.0. Please install from:") - print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + print("https://github.com/tiran/pysha3") sys.exit(1) # Checksum is built like so: @@ -28,7 +32,11 @@ if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): PREFIX = ".onion checksum".encode() # 32 bytes ed25519 pubkey from first test vector of # https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-02#section-6 -PUBKEY = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a".decode('hex') +PUBKEY_STRING = "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a" +if sys.version_info < (3, 0): + PUBKEY = PUBKEY_STRING.decode('hex') +else: + PUBKEY = bytes.fromhex(PUBKEY_STRING) # Version 3 is proposal224 VERSION = 3 diff --git a/src/test/hs_ntor_ref.py b/src/test/hs_ntor_ref.py index f107cc36ca..98025dd584 100644 --- a/src/test/hs_ntor_ref.py +++ b/src/test/hs_ntor_ref.py @@ -70,14 +70,16 @@ except ImportError: try: # Pull the sha3 functions in. from hashlib import sha3_256, shake_256 - shake_squeeze = shake_256.digest + def shake_squeeze(obj, n): + return obj.digest(n) except ImportError: if hasattr(sha3, "SHA3256"): # If this happens, then we have the old "sha3" module which # hashlib and pysha3 superseded. sha3_256 = sha3.SHA3256 shake_256 = sha3.SHAKE256 - shake_squeeze = shake_256.squeeze + def shake_squeeze(obj, n): + return obj.squeeze(n) else: # error code 77 tells automake to skip this test sys.exit(77) diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c index e9aafa4760..e1ecf9fe56 100644 --- a/src/test/hs_test_helpers.c +++ b/src/test/hs_test_helpers.c @@ -134,7 +134,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now, * points are added. */ static hs_descriptor_t * hs_helper_build_hs_desc_impl(unsigned int no_ip, - const ed25519_keypair_t *signing_kp) + const ed25519_keypair_t *signing_kp, + uint64_t rev_counter) { int ret; int i; @@ -161,7 +162,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip, &signing_kp->pubkey, now, 3600, CERT_FLAG_INCLUDE_SIGNING_KEY); tt_assert(desc->plaintext_data.signing_key_cert); - desc->plaintext_data.revision_counter = 42; + desc->plaintext_data.revision_counter = rev_counter; desc->plaintext_data.lifetime_sec = 3 * 60 * 60; hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey, @@ -226,18 +227,26 @@ hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp, subcred_out); } +/* Build a descriptor with a specific rev counter. */ +hs_descriptor_t * +hs_helper_build_hs_desc_with_rev_counter(const ed25519_keypair_t *signing_kp, + uint64_t revision_counter) +{ + return hs_helper_build_hs_desc_impl(0, signing_kp, revision_counter); +} + /* Build a descriptor with introduction points. */ hs_descriptor_t * hs_helper_build_hs_desc_with_ip(const ed25519_keypair_t *signing_kp) { - return hs_helper_build_hs_desc_impl(0, signing_kp); + return hs_helper_build_hs_desc_impl(0, signing_kp, 42); } /* Build a descriptor without any introduction points. */ hs_descriptor_t * hs_helper_build_hs_desc_no_ip(const ed25519_keypair_t *signing_kp) { - return hs_helper_build_hs_desc_impl(1, signing_kp); + return hs_helper_build_hs_desc_impl(1, signing_kp, 42); } hs_descriptor_t * @@ -247,7 +256,7 @@ hs_helper_build_hs_desc_with_client_auth( const ed25519_keypair_t *signing_kp) { curve25519_keypair_t auth_ephemeral_kp; - hs_descriptor_t *desc = hs_helper_build_hs_desc_impl(0, signing_kp); + hs_descriptor_t *desc = hs_helper_build_hs_desc_impl(0, signing_kp, 42); hs_desc_authorized_client_t *desc_client; /* The number of client authorized auth has tobe a multiple of diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h index 23d11f2a4a..e22295b660 100644 --- a/src/test/hs_test_helpers.h +++ b/src/test/hs_test_helpers.h @@ -17,6 +17,10 @@ hs_descriptor_t *hs_helper_build_hs_desc_no_ip( const ed25519_keypair_t *signing_kp); hs_descriptor_t *hs_helper_build_hs_desc_with_ip( const ed25519_keypair_t *signing_kp); +hs_descriptor_t * +hs_helper_build_hs_desc_with_rev_counter(const ed25519_keypair_t *signing_kp, + uint64_t revision_counter); + hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth( const uint8_t *descriptor_cookie, const curve25519_public_key_t *client_pk, diff --git a/src/test/test_config.c b/src/test/test_config.c index a0278f9422..49d7b87410 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -706,11 +706,13 @@ test_config_parse_tcp_proxy_line(void *arg) tor_free(msg); /* Bad TCPProxy line - unparsable address/port. */ - ret = parse_tcp_proxy_line("haproxy 95.216.163.36/443", options, &msg); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); + ret = parse_tcp_proxy_line("haproxy bogus_address!/300", options, &msg); tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "TCPProxy address/port failed to parse or resolve. " "Please fix."); tor_free(msg); + UNMOCK(tor_addr_lookup); /* Good TCPProxy line - ipv4. */ ret = parse_tcp_proxy_line("haproxy 95.216.163.36:443", options, &msg); @@ -723,7 +725,7 @@ test_config_parse_tcp_proxy_line(void *arg) tor_free(msg); done: - ; + UNMOCK(tor_addr_lookup); } /** @@ -6855,9 +6857,15 @@ test_config_duplicate_orports(void *arg) port_parse_config(ports, config_port, "OR", CONN_TYPE_OR_LISTENER, "[::]", 0, CL_PORT_SERVER_OPTIONS); - // There should be three ports at this point. - tt_int_op(smartlist_len(ports), OP_EQ, 3); + /* There should be 4 ports at this point that is: + * - 0.0.0.0:9050 + * - [::]:9050 + * - [::1]:9050 + * - [::1]:9050 + */ + tt_int_op(smartlist_len(ports), OP_EQ, 4); + /* This will remove the [::] and the extra [::1]. */ remove_duplicate_orports(ports); // The explicit IPv6 port should have replaced the implicit IPv6 port. @@ -6869,6 +6877,33 @@ test_config_duplicate_orports(void *arg) config_free_lines(config_port); } +static void +test_config_multifamily_port(void *arg) +{ + (void) arg; + + config_line_t *config_port = NULL; + smartlist_t *ports = smartlist_new(); + + config_line_append(&config_port, "SocksPort", "9050"); + config_line_append(&config_port, "SocksPort", "[::1]:9050"); + + // Parse IPv4, then IPv6. + port_parse_config(ports, config_port, "SOCKS", CONN_TYPE_AP_LISTENER, + "0.0.0.0", 9050, 0); + + /* There should be 2 ports at this point that is: + * - 0.0.0.0:9050 + * - [::1]:9050 + */ + tt_int_op(smartlist_len(ports), OP_EQ, 2); + + done: + SMARTLIST_FOREACH(ports, port_cfg_t *, cfg, port_cfg_free(cfg)); + smartlist_free(ports); + config_free_lines(config_port); +} + #ifndef COCCI #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -6937,5 +6972,6 @@ struct testcase_t config_tests[] = { CONFIG_TEST(kvline_parse, 0), CONFIG_TEST(getinfo_config_names, 0), CONFIG_TEST(duplicate_orports, 0), + CONFIG_TEST(multifamily_port, 0), END_OF_TESTCASES }; diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index 7935530653..fd260f06dc 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -24,60 +24,6 @@ #include <sys/stat.h> #endif -/* Test connection_or_remove_from_ext_or_id_map and - * connection_or_set_ext_or_identifier */ -static void -test_ext_or_id_map(void *arg) -{ - or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL; - char *idp = NULL, *idp2 = NULL; - (void)arg; - - /* pre-initialization */ - tt_ptr_op(NULL, OP_EQ, - connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); - - c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); - c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET); - c3 = or_connection_new(CONN_TYPE_OR, AF_INET); - - tt_ptr_op(c1->ext_or_conn_id, OP_NE, NULL); - tt_ptr_op(c2->ext_or_conn_id, OP_NE, NULL); - tt_ptr_op(c3->ext_or_conn_id, OP_EQ, NULL); - - tt_ptr_op(c1, OP_EQ, connection_or_get_by_ext_or_id(c1->ext_or_conn_id)); - tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(c2->ext_or_conn_id)); - tt_ptr_op(NULL, OP_EQ, - connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx")); - - idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); - - /* Give c2 a new ID. */ - connection_or_set_ext_or_identifier(c2); - tt_mem_op(idp, OP_NE, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); - idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN); - tt_assert(!tor_digest_is_zero(idp2)); - - tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp)); - tt_ptr_op(c2, OP_EQ, connection_or_get_by_ext_or_id(idp2)); - - /* Now remove it. */ - connection_or_remove_from_ext_or_id_map(c2); - tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp)); - tt_ptr_op(NULL, OP_EQ, connection_or_get_by_ext_or_id(idp2)); - - done: - if (c1) - connection_free_minimal(TO_CONN(c1)); - if (c2) - connection_free_minimal(TO_CONN(c2)); - if (c3) - connection_free_minimal(TO_CONN(c3)); - tor_free(idp); - tor_free(idp2); - connection_or_clear_ext_or_id_map(); -} - /* Simple connection_write_to_buf_impl_ replacement that unconditionally * writes to outbuf. */ static void @@ -581,7 +527,6 @@ test_ext_or_handshake(void *arg) } struct testcase_t extorport_tests[] = { - { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL }, { "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL }, { "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL }, { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL }, diff --git a/src/test/test_key_expiration.sh b/src/test/test_key_expiration.sh index 1ba8179aa1..2e2745e0a3 100755 --- a/src/test/test_key_expiration.sh +++ b/src/test/test_key_expiration.sh @@ -107,7 +107,7 @@ TOR="${TOR_BINARY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --Ex # Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there. echo "Setup step #1" -${TOR} --list-fingerprint ${SILENTLY} > /dev/null +${TOR} ${SILENTLY} --list-fingerprint > /dev/null check_dir "${DATA_DIR}/keys" check_file "${DATA_DIR}/keys/ed25519_master_id_public_key" diff --git a/src/test/test_keygen.sh b/src/test/test_keygen.sh index 6812f8883d..be1fde9e32 100755 --- a/src/test/test_keygen.sh +++ b/src/test/test_keygen.sh @@ -120,7 +120,7 @@ TOR="${TOR_BINARY} ${QUIETLY} --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort # Step 1: Start Tor with --list-fingerprint --quiet. Make sure everything is there. mkdir "${DATA_DIR}/orig" -${TOR} --DataDirectory "${DATA_DIR}/orig" --list-fingerprint ${SILENTLY} > /dev/null +${TOR} --DataDirectory "${DATA_DIR}/orig" ${SILENTLY} --list-fingerprint > /dev/null check_dir "${DATA_DIR}/orig/keys" check_file "${DATA_DIR}/orig/keys/ed25519_master_id_public_key" @@ -206,7 +206,7 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_"* "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Tor failed when starting with only master key" +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Tor failed when starting with only master key" check_files_eq "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/ed25519_master_id_public_key" check_files_eq "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/ed25519_master_id_secret_key" check_file "${ME}/keys/ed25519_signing_cert" @@ -264,11 +264,11 @@ SRC="${DATA_DIR}/orig" mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key" +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint > "${ME}/fp1" || die "Tor wouldn't start with only unencrypted secret key" check_file "${ME}/keys/ed25519_master_id_public_key" check_file "${ME}/keys/ed25519_signing_cert" check_file "${ME}/keys/ed25519_signing_secret_key" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key." +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint > "${ME}/fp2" || die "Tor wouldn't start again after starting once with only unencrypted secret key." check_files_eq "${ME}/fp1" "${ME}/fp2" @@ -330,7 +330,7 @@ cp "${SRC}/keys/ed25519_master_id_secret_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with missing public key" +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with missing public key" check_keys_eq ed25519_master_id_secret_key check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -352,7 +352,7 @@ cp "${SRC}/keys/ed25519_master_id_public_key" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with offline secret key" +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with offline secret key" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_keys_eq ed25519_master_id_public_key check_keys_eq ed25519_signing_secret_key @@ -373,7 +373,7 @@ mkdir -p "${ME}/keys" cp "${SRC}/keys/ed25519_signing_cert" "${ME}/keys/" cp "${SRC}/keys/ed25519_signing_secret_key" "${ME}/keys/" -${TOR} --DataDirectory "${ME}" --list-fingerprint ${SILENTLY} >/dev/null || die "Failed when starting with only signing material" +${TOR} --DataDirectory "${ME}" ${SILENTLY} --list-fingerprint >/dev/null || die "Failed when starting with only signing material" check_no_file "${ME}/keys/ed25519_master_id_secret_key" check_file "${ME}/keys/ed25519_master_id_public_key" check_keys_eq ed25519_signing_secret_key diff --git a/src/test/test_stats.c b/src/test/test_stats.c index b6849b0b6d..25ee837f2f 100644 --- a/src/test/test_stats.c +++ b/src/test/test_stats.c @@ -12,6 +12,8 @@ #include "lib/crypt_ops/crypto_rand.h" #include "app/config/or_state_st.h" #include "test/rng_test_helpers.h" +#include "feature/hs/hs_cache.h" +#include "test/hs_test_helpers.h" #include <stdio.h> @@ -31,6 +33,7 @@ #define MAINLOOP_PRIVATE #define STATEFILE_PRIVATE #define BWHIST_PRIVATE +#define REPHIST_PRIVATE #include "core/or/or.h" #include "lib/err/backtrace.h" @@ -493,6 +496,133 @@ test_get_bandwidth_lines(void *arg) bwhist_free_all(); } +static bool +mock_should_collect_v3_stats(void) +{ + return true; +} + +/* Test v3 metrics */ +static void +test_rephist_v3_onions(void *arg) +{ + int ret; + + char *stats_string = NULL; + char *desc1_str = NULL; + ed25519_keypair_t signing_kp1; + hs_descriptor_t *desc1 = NULL; + + const hs_v3_stats_t *hs_v3_stats = NULL; + + (void) arg; + + MOCK(should_collect_v3_stats, mock_should_collect_v3_stats); + + get_options_mutable()->HiddenServiceStatistics = 1; + + /* Initialize the subsystems */ + hs_cache_init(); + rep_hist_hs_stats_init(0); + + /* Change time to 03-01-2002 23:36 UTC */ + update_approx_time(1010101010); + + /* HS stats should be zero here */ + hs_v3_stats = rep_hist_get_hs_v3_stats(); + tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period), + OP_EQ, 0); + + /* Generate a valid descriptor */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, OP_EQ, 0); + desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 42); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Store descriptor and check that stats got updated */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + hs_v3_stats = rep_hist_get_hs_v3_stats(); + tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period), + OP_EQ, 1); + + /* cleanup */ + hs_descriptor_free(desc1); + tor_free(desc1_str); + + /* Generate another valid descriptor */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, OP_EQ, 0); + desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 42); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Store descriptor and check that stats are updated */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + hs_v3_stats = rep_hist_get_hs_v3_stats(); + tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period), + OP_EQ, 2); + + /* Check that storing the same descriptor twice does not work */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, -1); + + /* cleanup */ + hs_descriptor_free(desc1); + tor_free(desc1_str); + + /* Create a descriptor with the same identity key but diff rev counter and + same blinded key */ + desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 43); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Store descriptor and check that stats are updated */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period), + OP_EQ, 2); + + /* cleanup */ + hs_descriptor_free(desc1); + tor_free(desc1_str); + + /* Now let's skip to four days forward so that the blinded key rolls + forward */ + update_approx_time(approx_time() + 345600); + + /* Now create a descriptor with the same identity key but diff rev counter + and different blinded key */ + desc1 = hs_helper_build_hs_desc_with_rev_counter(&signing_kp1, 44); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, NULL, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Store descriptor and check that stats are updated */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(digest256map_size(hs_v3_stats->v3_onions_seen_this_period), + OP_EQ, 3); + + /* cleanup */ + hs_descriptor_free(desc1); + tor_free(desc1_str); + + /* Because of differential privacy we can't actually check the stat value, + but let's just check that it's formatted correctly. */ + stats_string = rep_hist_format_hs_stats(approx_time(), true); + tt_assert(strstr(stats_string, "hidserv-dir-v3-onions-seen")); + + done: + UNMOCK(should_collect_v3_stats); + tor_free(stats_string); +} + #define ENT(name) \ { #name, test_ ## name , 0, NULL, NULL } #define FORK(name) \ @@ -506,6 +636,7 @@ struct testcase_t stats_tests[] = { FORK(add_obs), FORK(fill_bandwidth_history), FORK(get_bandwidth_lines), + FORK(rephist_v3_onions), END_OF_TESTCASES }; diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 57ec53cf34..908ea08d3a 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -217,7 +217,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.4.5.1-alpha-dev" +#define VERSION "0.4.6.0-alpha-dev" #define HAVE_STRUCT_SOCKADDR_IN6 #define HAVE_STRUCT_IN6_ADDR |