diff options
Diffstat (limited to 'src')
51 files changed, 1255 insertions, 477 deletions
diff --git a/src/common/address.c b/src/common/address.c index 773e688554..fa6630ef92 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -2100,7 +2100,8 @@ get_interface_address,(int severity, uint32_t *addr)) } /** Return true if we can tell that <b>name</b> is a canonical name for the - * loopback address. */ + * loopback address. Return true also for *.local hostnames, which are + * multicast DNS names for hosts on the local network. */ int tor_addr_hostname_is_local(const char *name) { diff --git a/src/common/compat.c b/src/common/compat.c index 8d6a491c42..ebf05f59e1 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -204,7 +204,15 @@ tor_rename(const char *path_old, const char *path_new) sandbox_intern_string(path_new)); } -#if defined(HAVE_SYS_MMAN_H) || defined(RUNNING_DOXYGEN) +/* Some MinGW builds have sys/mman.h, but not the corresponding symbols. + * Other configs rename the symbols using macros (including getpagesize). + * So check for sys/mman.h and unistd.h, and a getpagesize declaration. */ +#if (defined(HAVE_SYS_MMAN_H) && defined(HAVE_UNISTD_H) && \ + defined(HAVE_DECL_GETPAGESIZE)) +#define COMPAT_HAS_MMAN_AND_PAGESIZE +#endif + +#if defined(COMPAT_HAS_MMAN_AND_PAGESIZE) || defined(RUNNING_DOXYGEN) /** Try to create a memory mapping for <b>filename</b> and return it. On * failure, return NULL. Sets errno properly, using ERANGE to mean * "empty file". */ diff --git a/src/common/crypto.c b/src/common/crypto.c index fff516cc8e..be42d36af6 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1506,7 +1506,7 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) if (crypto_pk_get_digest(pk, digest)) { return -1; } - if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) { + if (crypto_digest(hashed_digest, digest, DIGEST_LEN) < 0) { return -1; } base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); @@ -1700,19 +1700,21 @@ crypto_cipher_decrypt_with_iv(const char *key, /** Compute the SHA1 digest of the <b>len</b> bytes on data stored in * <b>m</b>. Write the DIGEST_LEN byte result into <b>digest</b>. - * Return 0 on success, 1 on failure. + * Return 0 on success, -1 on failure. */ int crypto_digest(char *digest, const char *m, size_t len) { tor_assert(m); tor_assert(digest); - return (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL); + if (SHA1((const unsigned char*)m,len,(unsigned char*)digest) == NULL) + return -1; + return 0; } /** Compute a 256-bit digest of <b>len</b> bytes in data stored in <b>m</b>, * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN256-byte result - * into <b>digest</b>. Return 0 on success, 1 on failure. */ + * into <b>digest</b>. Return 0 on success, -1 on failure. */ int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm) @@ -1720,16 +1722,22 @@ crypto_digest256(char *digest, const char *m, size_t len, tor_assert(m); tor_assert(digest); tor_assert(algorithm == DIGEST_SHA256 || algorithm == DIGEST_SHA3_256); + + int ret = 0; if (algorithm == DIGEST_SHA256) - return (SHA256((const uint8_t*)m,len,(uint8_t*)digest) == NULL); + ret = (SHA256((const uint8_t*)m,len,(uint8_t*)digest) != NULL); else - return (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len) - == -1); + ret = (sha3_256((uint8_t *)digest, DIGEST256_LEN,(const uint8_t *)m, len) + > -1); + + if (!ret) + return -1; + return 0; } /** Compute a 512-bit digest of <b>len</b> bytes in data stored in <b>m</b>, * using the algorithm <b>algorithm</b>. Write the DIGEST_LEN512-byte result - * into <b>digest</b>. Return 0 on success, 1 on failure. */ + * into <b>digest</b>. Return 0 on success, -1 on failure. */ int crypto_digest512(char *digest, const char *m, size_t len, digest_algorithm_t algorithm) @@ -1737,12 +1745,18 @@ crypto_digest512(char *digest, const char *m, size_t len, tor_assert(m); tor_assert(digest); tor_assert(algorithm == DIGEST_SHA512 || algorithm == DIGEST_SHA3_512); + + int ret = 0; if (algorithm == DIGEST_SHA512) - return (SHA512((const unsigned char*)m,len,(unsigned char*)digest) - == NULL); + ret = (SHA512((const unsigned char*)m,len,(unsigned char*)digest) + != NULL); else - return (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len) - == -1); + ret = (sha3_512((uint8_t*)digest, DIGEST512_LEN, (const uint8_t*)m, len) + > -1); + + if (!ret) + return -1; + return 0; } /** Set the common_digests_t in <b>ds_out</b> to contain every digest on the @@ -2628,7 +2642,7 @@ crypto_expand_key_material_TAP(const uint8_t *key_in, size_t key_in_len, for (cp = key_out, i=0; cp < key_out+key_out_len; ++i, cp += DIGEST_LEN) { tmp[key_in_len] = i; - if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1)) + if (crypto_digest((char*)digest, (const char *)tmp, key_in_len+1) < 0) goto exit; memcpy(cp, digest, MIN(DIGEST_LEN, key_out_len-(cp-key_out))); } diff --git a/src/common/sandbox.c b/src/common/sandbox.c index 24ba8a2997..ebc843e130 100644 --- a/src/common/sandbox.c +++ b/src/common/sandbox.c @@ -1579,13 +1579,14 @@ sandbox_add_addrinfo(const char *name) void sandbox_free_getaddrinfo_cache(void) { - cached_getaddrinfo_item_t **next, **item; + cached_getaddrinfo_item_t **next, **item, *this; for (item = HT_START(getaddrinfo_cache, &getaddrinfo_cache); item; item = next) { + this = *item; next = HT_NEXT_RMV(getaddrinfo_cache, &getaddrinfo_cache, item); - cached_getaddrinfo_item_free(*item); + cached_getaddrinfo_item_free(this); } HT_CLEAR(getaddrinfo_cache, &getaddrinfo_cache); diff --git a/src/common/timers.c b/src/common/timers.c index 41b2008ac4..e1ad47b15b 100644 --- a/src/common/timers.c +++ b/src/common/timers.c @@ -255,6 +255,20 @@ timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg) } /** + * Set *<b>cb_out</b> (if provided) to this timer's callback function, + * and *<b>arg_out</b> (if provided) to this timer's callback argument. + */ +void +timer_get_cb(const tor_timer_t *t, + timer_cb_fn_t *cb_out, void **arg_out) +{ + if (cb_out) + *cb_out = t->callback.cb; + if (arg_out) + *arg_out = t->callback.arg; +} + +/** * Schedule the timer t to fire at the current time plus a delay of * <b>delay</b> microseconds. All times are relative to monotime_get(). */ diff --git a/src/common/timers.h b/src/common/timers.h index 5f918f8e15..c5246a3335 100644 --- a/src/common/timers.h +++ b/src/common/timers.h @@ -13,6 +13,8 @@ typedef void (*timer_cb_fn_t)(tor_timer_t *, void *, const struct monotime_t *); tor_timer_t *timer_new(timer_cb_fn_t cb, void *arg); void timer_set_cb(tor_timer_t *t, timer_cb_fn_t cb, void *arg); +void timer_get_cb(const tor_timer_t *t, + timer_cb_fn_t *cb_out, void **arg_out); void timer_schedule(tor_timer_t *t, const struct timeval *delay); void timer_disable(tor_timer_t *t); void timer_free(tor_timer_t *t); diff --git a/src/common/util.c b/src/common/util.c index cb5f12821e..417aa89433 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2270,10 +2270,14 @@ check_private_dir,(const char *dirname, cpd_check_t check, * permissions on the directory will be checked again below.*/ fd = open(sandbox_intern_string(dirname), O_NOFOLLOW); - if (fd == -1) + if (fd == -1) { + log_warn(LD_FS, "Could not reopen recently created directory %s: %s", + dirname, + strerror(errno)); return -1; - else + } else { close(fd); + } } else if (!(check & CPD_CHECK)) { log_warn(LD_FS, "Directory %s does not exist.", dirname); @@ -2601,6 +2605,14 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) if (file_data->rename_on_close) { tor_assert(file_data->tempname && file_data->filename); + if (!abort_write) { + tor_assert(strcmp(file_data->filename, file_data->tempname)); + if (replace_file(file_data->tempname, file_data->filename)) { + log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, + strerror(errno)); + abort_write = r = -1; + } + } if (abort_write) { int res = unlink(file_data->tempname); if (res != 0) { @@ -2609,13 +2621,6 @@ finish_writing_to_file_impl(open_file_t *file_data, int abort_write) file_data->tempname, strerror(errno)); r = -1; } - } else { - tor_assert(strcmp(file_data->filename, file_data->tempname)); - if (replace_file(file_data->tempname, file_data->filename)) { - log_warn(LD_FS, "Error replacing \"%s\": %s", file_data->filename, - strerror(errno)); - r = -1; - } } } diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 96bd472dba..f60a8bfa89 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -1537,9 +1537,9 @@ circuit_get_unhandled_ports(time_t now) * If we're returning 0, set need_uptime and need_capacity to * indicate any requirements that the unhandled ports have. */ -int -circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity) +MOCK_IMPL(int, +circuit_all_predicted_ports_handled, (time_t now, int *need_uptime, + int *need_capacity)) { int i, enough; uint16_t *port; diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index f71c116eda..54d14bbc7f 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -40,8 +40,9 @@ int onionskin_answer(or_circuit_t *circ, const struct created_cell_t *created_cell, const char *keys, const uint8_t *rend_circ_nonce); -int circuit_all_predicted_ports_handled(time_t now, int *need_uptime, - int *need_capacity); +MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, + int *need_uptime, + int *need_capacity)); int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info); int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index eaecfd9dc0..04c5af92e8 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1022,8 +1022,117 @@ circuit_stream_is_being_handled(entry_connection_t *conn, /** Don't keep more than this many unused open circuits around. */ #define MAX_UNUSED_OPEN_CIRCUITS 14 -/** Figure out how many circuits we have open that are clean. Make - * sure it's enough for all the upcoming behaviors we predict we'll have. +/* Return true if a circuit is available for use, meaning that it is open, + * clean, usable for new multi-hop connections, and a general purpose origin + * circuit. + * Accept any kind of circuit, return false if the above conditions are not + * met. */ +STATIC int +circuit_is_available_for_use(const circuit_t *circ) +{ + const origin_circuit_t *origin_circ; + cpath_build_state_t *build_state; + + if (!CIRCUIT_IS_ORIGIN(circ)) + return 0; /* We first filter out only origin circuits before doing the + following checks. */ + if (circ->marked_for_close) + return 0; /* Don't mess with marked circs */ + if (circ->timestamp_dirty) + return 0; /* Only count clean circs */ + if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) + return 0; /* We only pay attention to general purpose circuits. + General purpose circuits are always origin circuits. */ + + origin_circ = CONST_TO_ORIGIN_CIRCUIT(circ); + if (origin_circ->unusable_for_new_conns) + return 0; + + build_state = origin_circ->build_state; + if (build_state->onehop_tunnel) + return 0; + + return 1; +} + +/* Return true if we need any more exit circuits. + * needs_uptime and needs_capacity are set only if we need more exit circuits. + * Check if we know of a port that's been requested recently and no circuit + * is currently available that can handle it. */ +STATIC int +needs_exit_circuits(time_t now, int *needs_uptime, int *needs_capacity) +{ + return (!circuit_all_predicted_ports_handled(now, needs_uptime, + needs_capacity) && + router_have_consensus_path() == CONSENSUS_PATH_EXIT); +} + +/* Hidden services need at least this many internal circuits */ +#define SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS 3 + +/* Return true if we need any more hidden service server circuits. + * HS servers only need an internal circuit. */ +STATIC int +needs_hs_server_circuits(int num_uptime_internal) +{ + return (num_rend_services() && + num_uptime_internal < SUFFICIENT_UPTIME_INTERNAL_HS_SERVERS && + router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN); +} + +/* We need at least this many internal circuits for hidden service clients */ +#define SUFFICIENT_INTERNAL_HS_CLIENTS 3 + +/* We need at least this much uptime for internal circuits for hidden service + * clients */ +#define SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS 2 + +/* Return true if we need any more hidden service client circuits. + * HS clients only need an internal circuit. */ +STATIC int +needs_hs_client_circuits(time_t now, int *needs_uptime, int *needs_capacity, + int num_internal, int num_uptime_internal) +{ + int used_internal_recently = rep_hist_get_predicted_internal(now, + needs_uptime, + needs_capacity); + int requires_uptime = num_uptime_internal < + SUFFICIENT_UPTIME_INTERNAL_HS_CLIENTS && + needs_uptime; + + return (used_internal_recently && + (requires_uptime || num_internal < SUFFICIENT_INTERNAL_HS_CLIENTS) && + router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN); +} + +/* The minimum number of open slots we should keep in order to preemptively + * build circuits. */ +#define CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS 2 + +/* Check to see if we need more circuits to have a good build timeout. However, + * leave a couple slots open so that we can still build circuits preemptively + * as needed. */ +#define CBT_MAX_UNUSED_OPEN_CIRCUITS (MAX_UNUSED_OPEN_CIRCUITS - \ + CBT_MIN_REMAINING_PREEMPTIVE_CIRCUITS) + +/* Return true if we need more circuits for a good build timeout. + * XXXX make the assumption that build timeout streams should be + * created whenever we can build internal circuits. */ +STATIC int +needs_circuits_for_build(int num) +{ + if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (num < CBT_MAX_UNUSED_OPEN_CIRCUITS && + !circuit_build_times_disabled() && + circuit_build_times_needs_circuits_now(get_circuit_build_times())) { + return 1; + } + } + return 0; +} + +/** Determine how many circuits we have open that are clean, + * Make sure it's enough for all the upcoming behaviors we predict we'll have. * But put an upper bound on the total number of circuits. */ static void @@ -1035,25 +1144,14 @@ circuit_predict_and_launch_new(void) time_t now = time(NULL); int flags = 0; - /* First, count how many of each type of circuit we have already. */ + /* Count how many of each type of circuit we currently have. */ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { - cpath_build_state_t *build_state; - origin_circuit_t *origin_circ; - if (!CIRCUIT_IS_ORIGIN(circ)) - continue; - if (circ->marked_for_close) - continue; /* don't mess with marked circs */ - if (circ->timestamp_dirty) - continue; /* only count clean circs */ - if (circ->purpose != CIRCUIT_PURPOSE_C_GENERAL) - continue; /* only pay attention to general-purpose circs */ - origin_circ = TO_ORIGIN_CIRCUIT(circ); - if (origin_circ->unusable_for_new_conns) - continue; - build_state = origin_circ->build_state; - if (build_state->onehop_tunnel) + if (!circuit_is_available_for_use(circ)) continue; + num++; + + cpath_build_state_t *build_state = TO_ORIGIN_CIRCUIT(circ)->build_state; if (build_state->is_internal) num_internal++; if (build_state->need_uptime && build_state->is_internal) @@ -1063,19 +1161,14 @@ circuit_predict_and_launch_new(void) /* If that's enough, then stop now. */ if (num >= MAX_UNUSED_OPEN_CIRCUITS) - return; /* we already have many, making more probably will hurt */ - - /* Second, see if we need any more exit circuits. */ - /* check if we know of a port that's been requested recently - * and no circuit is currently available that can handle it. - * Exits (obviously) require an exit circuit. */ - if (!circuit_all_predicted_ports_handled(now, &port_needs_uptime, - &port_needs_capacity) - && router_have_consensus_path() == CONSENSUS_PATH_EXIT) { + return; + + if (needs_exit_circuits(now, &port_needs_uptime, &port_needs_capacity)) { if (port_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; if (port_needs_capacity) flags |= CIRCLAUNCH_NEED_CAPACITY; + log_info(LD_CIRC, "Have %d clean circs (%d internal), need another exit circ.", num, num_internal); @@ -1083,12 +1176,10 @@ circuit_predict_and_launch_new(void) return; } - /* Third, see if we need any more hidden service (server) circuits. - * HS servers only need an internal circuit. */ - if (num_rend_services() && num_uptime_internal < 3 - && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (needs_hs_server_circuits(num_uptime_internal)) { flags = (CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL); + log_info(LD_CIRC, "Have %d clean circs (%d internal), need another internal " "circ for my hidden service.", @@ -1097,18 +1188,16 @@ circuit_predict_and_launch_new(void) return; } - /* Fourth, see if we need any more hidden service (client) circuits. - * HS clients only need an internal circuit. */ - if (rep_hist_get_predicted_internal(now, &hidserv_needs_uptime, - &hidserv_needs_capacity) && - ((num_uptime_internal<2 && hidserv_needs_uptime) || - num_internal<3) - && router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { + if (needs_hs_client_circuits(now, &hidserv_needs_uptime, + &hidserv_needs_capacity, + num_internal, num_uptime_internal)) + { if (hidserv_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; if (hidserv_needs_capacity) flags |= CIRCLAUNCH_NEED_CAPACITY; flags |= CIRCLAUNCH_IS_INTERNAL; + log_info(LD_CIRC, "Have %d clean circs (%d uptime-internal, %d internal), need" " another hidden service circ.", @@ -1117,26 +1206,17 @@ circuit_predict_and_launch_new(void) return; } - /* Finally, check to see if we still need more circuits to learn - * a good build timeout. But if we're close to our max number we - * want, don't do another -- we want to leave a few slots open so - * we can still build circuits preemptively as needed. - * XXXX make the assumption that build timeout streams should be - * created whenever we can build internal circuits. */ - if (router_have_consensus_path() != CONSENSUS_PATH_UNKNOWN) { - if (num < MAX_UNUSED_OPEN_CIRCUITS-2 && - ! circuit_build_times_disabled() && - circuit_build_times_needs_circuits_now(get_circuit_build_times())) { - flags = CIRCLAUNCH_NEED_CAPACITY; - /* if there are no exits in the consensus, make timeout - * circuits internal */ - if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) - flags |= CIRCLAUNCH_IS_INTERNAL; + if (needs_circuits_for_build(num)) { + flags = CIRCLAUNCH_NEED_CAPACITY; + /* if there are no exits in the consensus, make timeout + * circuits internal */ + if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) + flags |= CIRCLAUNCH_IS_INTERNAL; + log_info(LD_CIRC, "Have %d clean circs need another buildtime test circ.", num); circuit_launch(CIRCUIT_PURPOSE_C_GENERAL, flags); return; - } } } diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 5973978c45..d484be1986 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -59,5 +59,25 @@ int hostname_in_track_host_exits(const or_options_t *options, const char *address); void mark_circuit_unusable_for_new_conns(origin_circuit_t *circ); +#ifdef TOR_UNIT_TESTS +/* Used only by circuituse.c and test_circuituse.c */ + +STATIC int circuit_is_available_for_use(const circuit_t *circ); + +STATIC int needs_exit_circuits(time_t now, + int *port_needs_uptime, + int *port_needs_capacity); +STATIC int needs_hs_server_circuits(int num_uptime_internal); + +STATIC int needs_hs_client_circuits(time_t now, + int *needs_uptime, + int *needs_capacity, + int num_internal, + int num_uptime_internal); + +STATIC int needs_circuits_for_build(int num); + +#endif + #endif diff --git a/src/or/config.c b/src/or/config.c index d100af812c..6948bdbdb4 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1780,25 +1780,6 @@ 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(NULL)<0) { log_warn(LD_GENERAL,"Error loading rendezvous service keys"); @@ -2942,21 +2923,6 @@ options_validate_single_onion(or_options_t *options, char **msg) 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; } @@ -3089,7 +3055,7 @@ options_validate(or_options_t *old_options, or_options_t *options, } else if (!strcasecmp(options->TransProxyType, "ipfw")) { #ifndef KERNEL_MAY_SUPPORT_IPFW /* Earlier versions of OS X have ipfw */ - REJECT("ipfw is a FreeBSD-specific" + REJECT("ipfw is a FreeBSD-specific " "and OS X/Darwin-specific feature."); #else options->TransProxyType_parsed = TPT_IPFW; diff --git a/src/or/connection.c b/src/or/connection.c index f80602155b..ac3408a72e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1595,16 +1595,19 @@ connection_handle_listener_read(connection_t *conn, int new_type) /* remember the remote address */ tor_addr_copy(&newconn->addr, &addr); - newconn->port = port; - newconn->address = tor_addr_to_str_dup(&addr); + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + newconn->port = 0; + newconn->address = tor_strdup(conn->address); + } else { + newconn->port = port; + newconn->address = tor_addr_to_str_dup(&addr); + } if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) { log_info(LD_NET, "New SOCKS connection opened from %s.", fmt_and_decorate_addr(&addr)); } if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { - newconn->port = 0; - newconn->address = tor_strdup(conn->address); log_info(LD_NET, "New SOCKS AF_UNIX connection opened"); } if (new_type == CONN_TYPE_CONTROL) { diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 875c911f01..3874d52c23 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1627,11 +1627,9 @@ 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 */ - /* 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. */ if (socks->port == 0) { @@ -1771,7 +1769,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, rep_hist_note_used_resolve(now); /* help predict this next time */ /* no extra processing needed */ } else { - /* We should only be doing CONNECT or RESOLVE! */ + /* We should only be doing CONNECT, RESOLVE, or RESOLVE_PTR! */ tor_fragile_assert(); } diff --git a/src/or/control.c b/src/or/control.c index 96cc41bc4b..a22113174a 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1705,7 +1705,7 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS"); } else if (!strcmp(question, "address")) { uint32_t addr; - if (router_pick_published_address(get_options(), &addr) < 0) { + if (router_pick_published_address(get_options(), &addr, 0) < 0) { *errmsg = "Address unknown"; return -1; } @@ -2029,7 +2029,7 @@ getinfo_helper_dir(control_connection_t *control_conn, } else if (!strcmpstart(question, "dir/status/")) { *answer = tor_strdup(""); } else if (!strcmp(question, "dir/status-vote/current/consensus")) { /* v3 */ - if (directory_caches_dir_info(get_options())) { + if (we_want_to_fetch_flavor(get_options(), FLAV_NS)) { const cached_dir_t *consensus = dirserv_get_consensus("ns"); if (consensus) *answer = tor_strdup(consensus->dir); diff --git a/src/or/directory.c b/src/or/directory.c index 65ddd7d583..f0affbd395 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3988,10 +3988,12 @@ STATIC int next_random_exponential_delay(int delay, int max_delay) { /* Check preconditions */ + if (BUG(max_delay < 0)) + max_delay = 0; if (BUG(delay > max_delay)) delay = max_delay; - if (BUG(delay == INT_MAX)) - delay -= 1; /* prevent overflow */ + if (delay == INT_MAX) + return INT_MAX; /* prevent overflow */ if (BUG(delay < 0)) delay = 0; diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 0b896a2845..399d5ea955 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1033,7 +1033,8 @@ directory_fetches_from_authorities(const or_options_t *options) return 1; if (options->BridgeRelay == 1) return 0; - if (server_mode(options) && router_pick_published_address(options, &addr)<0) + if (server_mode(options) && + router_pick_published_address(options, &addr, 1) < 0) return 1; /* we don't know our IP address; ask an authority. */ refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); @@ -1068,8 +1069,10 @@ directory_fetches_dir_info_later(const or_options_t *options) return options->UseBridges != 0; } -/** Return true iff we want to fetch and keep certificates for authorities +/** Return true iff we want to serve certificates for authorities * that we don't acknowledge as authorities ourself. + * Use we_want_to_fetch_unknown_auth_certs to check if we want to fetch + * and keep these certificates. */ int directory_caches_unknown_auth_certs(const or_options_t *options) @@ -1077,11 +1080,14 @@ directory_caches_unknown_auth_certs(const or_options_t *options) return dir_server_mode(options) || options->BridgeRelay; } -/** Return 1 if we want to keep descriptors, networkstatuses, etc around. +/** Return 1 if we want to fetch and serve descriptors, networkstatuses, etc * Else return 0. * Check options->DirPort_set and directory_permits_begindir_requests() * to see if we are willing to serve these directory documents to others via * the DirPort and begindir-over-ORPort, respectively. + * + * To check if we should fetch documents, use we_want_to_fetch_flavor and + * we_want_to_fetch_unknown_auth_certs instead of this function. */ int directory_caches_dir_info(const or_options_t *options) diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 1517ccb12e..37aa1d745e 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -15,6 +15,7 @@ #include "ed25519_cert.h" /* Trunnel interface. */ #include "parsecommon.h" #include "rendcache.h" +#include "torcert.h" /* tor_cert_encode_ed22519() */ /* Constant string value used for the descriptor format. */ #define str_hs_desc "hs-descriptor" @@ -135,45 +136,6 @@ desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc) /* === ENCODING === */ -/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated - * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */ -STATIC int -encode_cert(const tor_cert_t *cert, char **cert_str_out) -{ - int ret = -1; - char *ed_cert_b64 = NULL; - size_t ed_cert_b64_len; - - tor_assert(cert); - tor_assert(cert_str_out); - - /* Get the encoded size and add the NUL byte. */ - ed_cert_b64_len = base64_encode_size(cert->encoded_len, - BASE64_ENCODE_MULTILINE) + 1; - ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len); - - /* Base64 encode the encoded certificate. */ - if (base64_encode(ed_cert_b64, ed_cert_b64_len, - (const char *) cert->encoded, cert->encoded_len, - BASE64_ENCODE_MULTILINE) < 0) { - log_err(LD_BUG, "Couldn't base64-encode descriptor signing key cert!"); - goto err; - } - - /* Put everything together in a NUL terminated string. */ - tor_asprintf(cert_str_out, - "-----BEGIN ED25519 CERT-----\n" - "%s" - "-----END ED25519 CERT-----", - ed_cert_b64); - /* Success! */ - ret = 0; - - err: - tor_free(ed_cert_b64); - return ret; -} - /* Encode the given link specifier objects into a newly allocated string. * This can't fail so caller can always assume a valid string being * returned. */ @@ -327,7 +289,7 @@ encode_enc_key(const ed25519_keypair_t *sig_key, if (!cross_cert) { goto err; } - ret = encode_cert(cross_cert, &encoded_cert); + ret = tor_cert_encode_ed22519(cross_cert, &encoded_cert); tor_cert_free(cross_cert); if (ret) { goto err; @@ -375,7 +337,7 @@ encode_intro_point(const ed25519_keypair_t *sig_key, /* Authentication key encoding. */ { char *encoded_cert; - if (encode_cert(ip->auth_key_cert, &encoded_cert) < 0) { + if (tor_cert_encode_ed22519(ip->auth_key_cert, &encoded_cert) < 0) { goto err; } smartlist_add_asprintf(lines, "%s\n%s", str_ip_auth_key, encoded_cert); @@ -769,7 +731,7 @@ desc_encode_v3(const hs_descriptor_t *desc, char **encoded_out) "(%d)", (int) desc->plaintext_data.signing_key_cert->cert_type); goto err; } - if (encode_cert(desc->plaintext_data.signing_key_cert, + if (tor_cert_encode_ed22519(desc->plaintext_data.signing_key_cert, &encoded_cert) < 0) { /* The function will print error logs. */ goto err; @@ -1394,15 +1356,10 @@ decode_intro_points(const hs_descriptor_t *desc, retval = 0; err: - if (chunked_desc) { - SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a)); - smartlist_free(chunked_desc); - } - if (intro_points) { - SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a)); - smartlist_free(intro_points); - } - + SMARTLIST_FOREACH(chunked_desc, char *, a, tor_free(a)); + smartlist_free(chunked_desc); + SMARTLIST_FOREACH(intro_points, char *, a, tor_free(a)); + smartlist_free(intro_points); return retval; } /* Return 1 iff the given base64 encoded signature in b64_sig from the encoded diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 895bed2485..083d353860 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -216,7 +216,6 @@ size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data); #ifdef HS_DESCRIPTOR_PRIVATE /* Encoding. */ -STATIC int encode_cert(const tor_cert_t *cert, char **cert_str_out); STATIC char *encode_link_specifiers(const smartlist_t *specs); STATIC size_t build_plaintext_padding(const char *plaintext, size_t plaintext_len, diff --git a/src/or/include.am b/src/or/include.am index 10f8b85bdf..99912a9947 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -206,7 +206,7 @@ noinst_HEADERS+= $(ORHEADERS) micro-revision.i micro-revision.i: FORCE $(AM_V_at)rm -f micro-revision.tmp; \ - if test -d "$(top_srcdir)/.git" && \ + if test -r "$(top_srcdir)/.git" && \ test -x "`which git 2>&1;true`"; then \ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \ echo \"$$HASH\" > micro-revision.tmp; \ diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 316ce48387..bfb36413ce 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -814,8 +814,11 @@ networkstatus_nickname_is_unnamed(const char *nickname) #define NONAUTHORITY_NS_CACHE_INTERVAL (60*60) /** Return true iff, given the options listed in <b>options</b>, <b>flavor</b> - * is the flavor of a consensus networkstatus that we would like to fetch. */ -static int + * is the flavor of a consensus networkstatus that we would like to fetch. + * + * For certificate fetches, use we_want_to_fetch_unknown_auth_certs, and + * for serving fetched documents, use directory_caches_dir_info. */ +int we_want_to_fetch_flavor(const or_options_t *options, int flavor) { if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) { @@ -837,6 +840,29 @@ we_want_to_fetch_flavor(const or_options_t *options, int flavor) return flavor == usable_consensus_flavor(); } +/** Return true iff, given the options listed in <b>options</b>, we would like + * to fetch and store unknown authority certificates. + * + * For consensus and descriptor fetches, use we_want_to_fetch_flavor, and + * for serving fetched certificates, use directory_caches_unknown_auth_certs. + */ +int +we_want_to_fetch_unknown_auth_certs(const or_options_t *options) +{ + if (authdir_mode_v3(options) || + directory_caches_unknown_auth_certs((options))) { + /* We want to serve all certs to others, regardless if we would use + * them ourselves. */ + return 1; + } + if (options->FetchUselessDescriptors) { + /* Unknown certificates are definitely useless. */ + return 1; + } + /* Otherwise, don't fetch unknown certificates. */ + return 0; +} + /** How long will we hang onto a possibly live consensus for which we're * fetching certs before we check whether there is a better one? */ #define DELAY_WHILE_FETCHING_CERTS (20*60) @@ -1728,9 +1754,9 @@ networkstatus_set_current_consensus(const char *consensus, } if (flav != usable_consensus_flavor() && - !directory_caches_dir_info(options)) { - /* This consensus is totally boring to us: we won't use it, and we won't - * serve it. Drop it. */ + !we_want_to_fetch_flavor(options, flav)) { + /* This consensus is totally boring to us: we won't use it, we didn't want + * it, and we won't serve it. Drop it. */ goto done; } @@ -1932,7 +1958,7 @@ networkstatus_set_current_consensus(const char *consensus, download_status_failed(&consensus_dl_status[flav], 0); } - if (directory_caches_dir_info(options)) { + if (we_want_to_fetch_flavor(options, flav)) { dirserv_set_cached_consensus_networkstatus(consensus, flavor, &c->digests, @@ -2381,9 +2407,9 @@ int client_would_use_router(const routerstatus_t *rs, time_t now, const or_options_t *options) { - if (!rs->is_flagged_running && !options->FetchUselessDescriptors) { + if (!rs->is_flagged_running) { /* If we had this router descriptor, we wouldn't even bother using it. - * But, if we want to have a complete list, fetch it anyway. */ + * (Fetching and storing depends on by we_want_to_fetch_flavor().) */ return 0; } if (rs->published_on + options->TestingEstimatedDescriptorPropagationTime diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index 71f36b69ed..454356e0bb 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -66,6 +66,8 @@ const routerstatus_t *router_get_consensus_status_by_nickname( int warn_if_unnamed); const char *networkstatus_get_router_digest_by_nickname(const char *nickname); int networkstatus_nickname_is_unnamed(const char *nickname); +int we_want_to_fetch_flavor(const or_options_t *options, int flavor); +int we_want_to_fetch_unknown_auth_certs(const or_options_t *options); void networkstatus_consensus_download_failed(int status_code, const char *flavname); void update_consensus_networkstatus_fetch_time(time_t now); diff --git a/src/or/nodelist.c b/src/or/nodelist.c index d8d2dbbfd4..6117b86d65 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -1639,8 +1639,8 @@ router_have_minimum_dir_info(void) * this can cause router_have_consensus_path() to be set to * CONSENSUS_PATH_EXIT, even if there are no nodes with accept exit policies. */ -consensus_path_type_t -router_have_consensus_path(void) +MOCK_IMPL(consensus_path_type_t, +router_have_consensus_path, (void)) { return have_consensus_path; } diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 31fdc64663..8456d21c6c 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -123,7 +123,8 @@ typedef enum { * create exit and internal paths, circuits, streams, ... */ CONSENSUS_PATH_EXIT = 1 } consensus_path_type_t; -consensus_path_type_t router_have_consensus_path(void); + +MOCK_DECL(consensus_path_type_t, router_have_consensus_path, (void)); void router_dir_info_changed(void); const char *get_dir_info_status_string(void); diff --git a/src/or/protover.c b/src/or/protover.c index 5972e61be7..ceaf2d5ccf 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -697,7 +697,7 @@ 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")) { + } else if (tor_version_as_new_as(version, "0.2.9.1-alpha")) { /* 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 " diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 0819d8a713..545fba1449 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -76,6 +76,13 @@ static ssize_t rend_service_parse_intro_for_v3( static int rend_service_check_private_dir(const or_options_t *options, const rend_service_t *s, int create); +static int rend_service_check_private_dir_impl(const or_options_t *options, + const rend_service_t *s, + int create); +static const smartlist_t* rend_get_service_list( + const smartlist_t* substitute_service_list); +static smartlist_t* rend_get_service_list_mutable( + smartlist_t* substitute_service_list); /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. @@ -121,8 +128,44 @@ static const char *hostname_fname = "hostname"; static const char *client_keys_fname = "client_keys"; static const char *sos_poison_fname = "onion_service_non_anonymous"; +/** A list of rend_service_t's for services run on this OP. + */ +static smartlist_t *rend_service_list = NULL; + +/* Like rend_get_service_list_mutable, but returns a read-only list. */ +static const smartlist_t* +rend_get_service_list(const smartlist_t* substitute_service_list) +{ + /* It is safe to cast away the const here, because + * rend_get_service_list_mutable does not actually modify the list */ + return rend_get_service_list_mutable((smartlist_t*)substitute_service_list); +} + +/* Return a mutable list of hidden services. + * If substitute_service_list is not NULL, return it. + * Otherwise, check if the global rend_service_list is non-NULL, and if so, + * return it. + * Otherwise, log a BUG message and return NULL. + * */ +static smartlist_t* +rend_get_service_list_mutable(smartlist_t* substitute_service_list) +{ + if (substitute_service_list) { + return substitute_service_list; + } + + /* If no special service list is provided, then just use the global one. */ + + if (BUG(!rend_service_list)) { + /* No global HS list, which is a programmer error. */ + return NULL; + } + + return rend_service_list; +} + /** Tells if onion service <b>s</b> is ephemeral. -*/ + */ static unsigned int rend_service_is_ephemeral(const struct rend_service_t *s) { @@ -137,10 +180,6 @@ rend_service_escaped_dir(const struct rend_service_t *s) return rend_service_is_ephemeral(s) ? "[EPHEMERAL]" : escaped(s->directory); } -/** A list of rend_service_t's for services run on this OP. - */ -static smartlist_t *rend_service_list = NULL; - /** Return the number of rendezvous services we have configured. */ int num_rend_services(void) @@ -225,21 +264,32 @@ rend_service_free_all(void) rend_service_list = NULL; } -/** Validate <b>service</b> and add it to rend_service_list if possible. +/** Validate <b>service</b> and add it to <b>service_list</b>, or to + * the global rend_service_list if <b>service_list</b> is NULL. * Return 0 on success. On failure, free <b>service</b> and return -1. + * Takes ownership of <b>service</b>. */ static int -rend_add_service(rend_service_t *service) +rend_add_service(smartlist_t *service_list, rend_service_t *service) { int i; rend_service_port_config_t *p; + tor_assert(service); + + smartlist_t *s_list = rend_get_service_list_mutable(service_list); + /* We must have a service list, even if it's a temporary one, so we can + * check for duplicate services */ + if (BUG(!s_list)) { + return -1; + } + service->intro_nodes = smartlist_new(); service->expiring_nodes = smartlist_new(); if (service->max_streams_per_circuit < 0) { log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max " - "streams per circuit; ignoring.", + "streams per circuit.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; @@ -248,24 +298,24 @@ rend_add_service(rend_service_t *service) if (service->max_streams_close_circuit < 0 || service->max_streams_close_circuit > 1) { log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid " - "max streams handling; ignoring.", + "max streams handling.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } if (service->auth_type != REND_NO_AUTH && - smartlist_len(service->clients) == 0) { + (!service->clients || + smartlist_len(service->clients) == 0)) { log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no " - "clients; ignoring.", + "clients.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } - if (!smartlist_len(service->ports)) { - log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; " - "ignoring.", + if (!service->ports || !smartlist_len(service->ports)) { + log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; @@ -286,20 +336,20 @@ 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. */ + tor_assert(s_list); if (!rend_service_is_ephemeral(service)) { /* Skip dupe for ephemeral services. */ - SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr, + SMARTLIST_FOREACH(s_list, rend_service_t*, ptr, dupe = dupe || !strcmp(ptr->directory, service->directory)); if (dupe) { log_warn(LD_REND, "Another hidden service is already configured for " - "directory %s, ignoring.", + "directory %s.", rend_service_escaped_dir(service)); rend_service_free(service); return -1; } } - smartlist_add(rend_service_list, service); log_debug(LD_REND,"Configuring service with directory %s", rend_service_escaped_dir(service)); for (i = 0; i < smartlist_len(service->ports); ++i) { @@ -315,14 +365,19 @@ rend_add_service(rend_service_t *service) "Service maps port %d to socket at \"%s\"", p->virtual_port, p->unix_addr); #else - log_debug(LD_REND, - "Service maps port %d to an AF_UNIX socket, but we " - "have no AF_UNIX support on this platform. This is " - "probably a bug.", - p->virtual_port); + log_warn(LD_BUG, + "Service maps port %d to an AF_UNIX socket, but we " + "have no AF_UNIX support on this platform. This is " + "probably a bug.", + p->virtual_port); + rend_service_free(service); + return -1; #endif /* defined(HAVE_SYS_UN_H) */ } } + /* The service passed all the checks */ + tor_assert(s_list); + smartlist_add(s_list, service); return 0; } /* NOTREACHED */ @@ -344,9 +399,9 @@ rend_service_port_config_new(const char *socket_path) return conf; } -/** Parses a real-port to virtual-port mapping separated by the provided - * separator and returns a new rend_service_port_config_t, or NULL and an - * optional error string on failure. +/** Parses a virtual-port to real-port/socket mapping separated by + * the provided separator and returns a new rend_service_port_config_t, + * or NULL and an optional error string on failure. * * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)? * @@ -371,14 +426,12 @@ rend_service_parse_port_config(const char *string, const char *sep, smartlist_split_string(sl, string, sep, 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."); + 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) - tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " + tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service " "port configuration", escaped(smartlist_get(sl,0))); goto err; @@ -406,10 +459,8 @@ rend_service_parse_port_config(const char *string, const char *sep, } else if (strchr(addrport, ':') || strchr(addrport, '.')) { /* else try it as an IP:port pair if it has a : or . in it */ if (tor_addr_port_lookup(addrport, &addr, &p)<0) { - if (err_msg_out) - err_msg = tor_strdup("Unparseable address in hidden service port " - "configuration."); - + err_msg = tor_strdup("Unparseable address in hidden service port " + "configuration."); goto err; } realport = p?p:virtport; @@ -417,11 +468,9 @@ rend_service_parse_port_config(const char *string, const char *sep, /* No addr:port, no addr -- must be port. */ realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL); if (!realport) { - if (err_msg_out) - tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " - "hidden service port configuration.", - escaped(addrport)); - + tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in " + "hidden service port configuration.", + escaped(addrport)); goto err; } tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */ @@ -440,7 +489,11 @@ rend_service_parse_port_config(const char *string, const char *sep, err: tor_free(addrport); - if (err_msg_out) *err_msg_out = err_msg; + if (err_msg_out != NULL) { + *err_msg_out = err_msg; + } else { + tor_free(err_msg); + } SMARTLIST_FOREACH(sl, char *, c, tor_free(c)); smartlist_free(sl); @@ -454,6 +507,41 @@ rend_service_port_config_free(rend_service_port_config_t *p) tor_free(p); } +/* Check the directory for <b>service</b>, and add the service to + * <b>service_list</b>, or to the global list if <b>service_list</b> is NULL. + * Only add the service to the list if <b>validate_only</b> is false. + * If <b>validate_only</b> is true, free the service. + * If <b>service</b> is NULL, ignore it, and return 0. + * Returns 0 on success, and -1 on failure. + * Takes ownership of <b>service</b>, either freeing it, or adding it to the + * global service list. + */ +STATIC int +rend_service_check_dir_and_add(smartlist_t *service_list, + const or_options_t *options, + rend_service_t *service, + int validate_only) +{ + if (!service) { + /* It is ok for a service to be NULL, this means there are no services */ + return 0; + } + + if (rend_service_check_private_dir(options, service, !validate_only) + < 0) { + rend_service_free(service); + return -1; + } + + smartlist_t *s_list = rend_get_service_list_mutable(service_list); + /* We must have a service list, even if it's a temporary one, so we can + * check for duplicate services */ + if (BUG(!s_list)) { + return -1; + } + return rend_add_service(s_list, service); +} + /** Set up rend_service_list, based on the values of HiddenServiceDir and * HiddenServicePort in <b>options</b>. Return 0 on success and -1 on * failure. (If <b>validate_only</b> is set, parse, warn and return as @@ -466,25 +554,21 @@ rend_config_services(const or_options_t *options, int validate_only) rend_service_t *service = NULL; rend_service_port_config_t *portcfg; smartlist_t *old_service_list = NULL; + smartlist_t *temp_service_list = NULL; int ok = 0; - if (!validate_only) { - old_service_list = rend_service_list; - rend_service_list = smartlist_new(); - } + /* Use a temporary service list, so that we can check the new services' + * consistency with each other */ + temp_service_list = smartlist_new(); for (line = options->RendConfigLines; line; line = line->next) { if (!strcasecmp(line->key, "HiddenServiceDir")) { - if (service) { /* register the one we just finished parsing */ - if (rend_service_check_private_dir(options, service, 0) < 0) { - rend_service_free(service); + /* register the service we just finished parsing + * this code registers every service except the last one parsed, + * which is registered below the loop */ + if (rend_service_check_dir_and_add(temp_service_list, options, service, + validate_only) < 0) { return -1; - } - - if (validate_only) - rend_service_free(service); - else - rend_add_service(service); } service = tor_malloc_zero(sizeof(rend_service_t)); service->directory = tor_strdup(line->value); @@ -695,19 +779,30 @@ rend_config_services(const or_options_t *options, int validate_only) } } } - if (service) { - if (rend_service_check_private_dir(options, service, 0) < 0) { - rend_service_free(service); - return -1; - } + /* register the final service after we have finished parsing all services + * this code only registers the last service, other services are registered + * within the loop. It is ok for this service to be NULL, it is ignored. */ + if (rend_service_check_dir_and_add(temp_service_list, options, service, + validate_only) < 0) { + return -1; + } - if (validate_only) { - rend_service_free(service); - } else { - rend_add_service(service); - } + /* Free the newly added services if validating */ + if (validate_only) { + SMARTLIST_FOREACH(temp_service_list, rend_service_t *, ptr, + rend_service_free(ptr)); + smartlist_free(temp_service_list); + temp_service_list = NULL; + return 0; } + /* Otherwise, use the newly added services as the new service list + * Since we have now replaced the global service list, from this point on we + * must succeed, or die trying. */ + old_service_list = rend_service_list; + rend_service_list = temp_service_list; + temp_service_list = NULL; + /* If this is a reload and there were hidden services configured before, * keep the introduction points that are still needed and close the * other ones. */ @@ -729,7 +824,7 @@ rend_config_services(const or_options_t *options, int validate_only) * will NOT have their intro point closed. */ SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, { - if (!old->directory) { + if (rend_service_is_ephemeral(old)) { SMARTLIST_DEL_CURRENT(old_service_list, old); smartlist_add(surviving_services, old); smartlist_add(rend_service_list, old); @@ -741,15 +836,20 @@ rend_config_services(const or_options_t *options, int validate_only) * probably ok? */ SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) { SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) { - if (new->directory && old->directory && - !strcmp(old->directory, new->directory)) { - smartlist_add_all(new->intro_nodes, old->intro_nodes); - smartlist_clear(old->intro_nodes); - smartlist_add_all(new->expiring_nodes, old->expiring_nodes); - smartlist_clear(old->expiring_nodes); - smartlist_add(surviving_services, old); - break; + if (BUG(rend_service_is_ephemeral(new)) || + BUG(rend_service_is_ephemeral(old))) { + continue; + } + if (BUG(!new->directory) || BUG(!old->directory) || + strcmp(old->directory, new->directory)) { + continue; } + smartlist_add_all(new->intro_nodes, old->intro_nodes); + smartlist_clear(old->intro_nodes); + smartlist_add_all(new->expiring_nodes, old->expiring_nodes); + smartlist_clear(old->expiring_nodes); + smartlist_add(surviving_services, old); + break; } SMARTLIST_FOREACH_END(old); } SMARTLIST_FOREACH_END(new); @@ -861,7 +961,7 @@ rend_service_add_ephemeral(crypto_pk_t *pk, } /* Initialize the service. */ - if (rend_add_service(s)) { + if (rend_add_service(NULL, s)) { return RSAE_INTERNAL; } *service_id_out = tor_strdup(s->service_id); @@ -1011,6 +1111,11 @@ service_is_single_onion_poisoned(const rend_service_t *service) char *poison_fname = NULL; file_status_t fstatus; + /* Passing a NULL service is a bug */ + if (BUG(!service)) { + return 0; + } + if (rend_service_is_ephemeral(service)) { return 0; } @@ -1044,58 +1149,69 @@ rend_service_private_key_exists(const rend_service_t *service) 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, +/** Check the single onion service poison state of the directory for s: + * - If the 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, + * - If the 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) + * - Otherwise, the poison state is invalid: the service was created in one + * mode, and is being used in the other, return -1. + * Hidden service directories without keys are always considered consistent. + * They will be poisoned after their directory is created (if needed). */ +STATIC int +rend_service_verify_single_onion_poison(const rend_service_t* s, + 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; - } + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; + } - s_list = rend_service_list; - } else { - s_list = service_list; + /* Ephemeral services are checked at ADD_ONION time */ + if (BUG(rend_service_is_ephemeral(s))) { + return -1; } - 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); + /* Service is expected to have a directory */ + if (BUG(!s->directory)) { + return -1; + } - return consistent ? 0 : -1; + /* Services without keys are always ok - their keys will only ever be used + * in the current mode */ + if (!rend_service_private_key_exists(s)) { + return 0; + } + + /* The key has been used before in a different mode */ + if (service_is_single_onion_poisoned(s) != + rend_service_non_anonymous_mode_enabled(options)) { + return -1; + } + + /* The key exists and is consistent with the current mode */ + return 0; } -/*** 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. +/*** Helper for rend_service_poison_new_single_onion_dir(). Add a file to + * the hidden service directory for s that marks it as a single onion service. + * Tor must be in single onion mode before calling this function, and the + * service directory must already have been created. * 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) +poison_new_single_onion_hidden_service_dir_impl(const rend_service_t *service, + const or_options_t* options) { + /* Passing a NULL service is a bug */ + if (BUG(!service)) { + return -1; + } + /* We must only poison directories if we're in Single Onion mode */ - tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + tor_assert(rend_service_non_anonymous_mode_enabled(options)); int fd; int retval = -1; @@ -1113,8 +1229,8 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service) return -1; } - /* Make sure the directory exists */ - if (rend_service_check_private_dir(get_options(), service, 1) < 0) + /* Make sure the directory was created before calling this function. */ + if (BUG(rend_service_check_private_dir_impl(options, service, 0) < 0)) return -1; poison_fname = rend_service_sos_poison_path(service); @@ -1151,44 +1267,39 @@ poison_new_single_onion_hidden_service_dir(const rend_service_t *service) 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). +/** We just got launched in Single Onion Mode. That's a non-anonymous mode for + * hidden services. If s is new, we should mark its hidden service + * directory appropriately so that it is never launched as a location-private + * hidden service. (New directories don't have private key files.) * Return 0 on success, -1 on fail. */ -int -rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) +STATIC int +rend_service_poison_new_single_onion_dir(const rend_service_t *s, + const or_options_t* options) { + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; + } + /* We must only poison directories if we're in Single Onion mode */ - tor_assert(rend_service_non_anonymous_mode_enabled(get_options())); + tor_assert(rend_service_non_anonymous_mode_enabled(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; - } + /* Ephemeral services aren't allowed in non-anonymous mode */ + if (BUG(rend_service_is_ephemeral(s))) { + return -1; + } - s_list = rend_service_list; - } else { - s_list = service_list; + /* Service is expected to have a directory */ + if (BUG(!s->directory)) { + return -1; } - 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; - } + if (!rend_service_private_key_exists(s)) { + if (poison_new_single_onion_hidden_service_dir_impl(s, options) + < 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; } @@ -1202,13 +1313,10 @@ rend_service_poison_new_single_onion_dirs(const smartlist_t *service_list) int rend_service_load_all_keys(const smartlist_t *service_list) { - 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; + /* Use service_list for unit tests */ + const smartlist_t *s_list = rend_get_service_list(service_list); + if (BUG(!s_list)) { + return -1; } SMARTLIST_FOREACH_BEGIN(s_list, rend_service_t *, s) { @@ -1272,6 +1380,32 @@ rend_service_derive_key_digests(struct rend_service_t *s) return 0; } +/* Implements the directory check from rend_service_check_private_dir, + * without doing the single onion poison checks. */ +static int +rend_service_check_private_dir_impl(const or_options_t *options, + const rend_service_t *s, + int create) +{ + cpd_check_t check_opts = CPD_NONE; + if (create) { + check_opts |= CPD_CREATE; + } else { + check_opts |= CPD_CHECK_MODE_ONLY; + check_opts |= CPD_CHECK; + } + if (s->dir_group_readable) { + check_opts |= CPD_GROUP_READ; + } + /* Check/create directory */ + if (check_private_dir(s->directory, check_opts, options->User) < 0) { + log_warn(LD_REND, "Checking service directory %s failed.", s->directory); + return -1; + } + + return 0; +} + /** Make sure that the directory for <b>s</b> is private, using the config in * <b>options</b>. * If <b>create</b> is true: @@ -1286,20 +1420,58 @@ rend_service_check_private_dir(const or_options_t *options, const rend_service_t *s, int create) { - cpd_check_t check_opts = CPD_NONE; - if (create) { - check_opts |= CPD_CREATE; - } else { - check_opts |= CPD_CHECK_MODE_ONLY; - check_opts |= CPD_CHECK; - } - if (s->dir_group_readable) { - check_opts |= CPD_GROUP_READ; + /* Passing a NULL service is a bug */ + if (BUG(!s)) { + return -1; } + /* Check/create directory */ - if (check_private_dir(s->directory, check_opts, options->User) < 0) { + if (rend_service_check_private_dir_impl(options, s, create) < 0) { + return -1; + } + + /* Check if the hidden service key exists, and was created in a different + * single onion service mode, and refuse to launch if it has. + * This is safe to call even when create is false, as it ignores missing + * keys and directories: they are always valid. + */ + if (rend_service_verify_single_onion_poison(s, options) < 0) { + /* We can't use s->service_id here, as the key may not have been loaded */ + log_warn(LD_GENERAL, "We are configured with " + "HiddenServiceNonAnonymousMode %d, but the hidden " + "service key in directory %s was created in %s mode. " + "This is not allowed.", + rend_service_non_anonymous_mode_enabled(options) ? 1 : 0, + rend_service_escaped_dir(s), + rend_service_non_anonymous_mode_enabled(options) ? + "an anonymous" : "a non-anonymous" + ); return -1; } + + /* Poison new single onion directories immediately after they are created, + * so that we never accidentally launch non-anonymous hidden services + * thinking they are anonymous. Any keys created later will end up with the + * correct poisoning state. + */ + if (create && rend_service_non_anonymous_mode_enabled(options)) { + static int logged_warning = 0; + + if (rend_service_poison_new_single_onion_dir(s, options) < 0) { + log_warn(LD_GENERAL,"Failed to mark new hidden services as non-anonymous" + "."); + return -1; + } + + if (!logged_warning) { + /* 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."); + logged_warning = 1; + } + } + return 0; } @@ -1312,7 +1484,9 @@ rend_service_load_keys(rend_service_t *s) char *fname = NULL; char buf[128]; - if (rend_service_check_private_dir(get_options(), s, 1) < 0) + /* Make sure the directory was created and single onion poisoning was + * checked before calling this function */ + if (BUG(rend_service_check_private_dir(get_options(), s, 0) < 0)) goto err; /* Load key */ @@ -3086,7 +3260,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit) len += 2; memcpy(auth, circuit->cpath->prev->rend_circ_nonce, DIGEST_LEN); memcpy(auth+DIGEST_LEN, "INTRODUCE", 9); - if (crypto_digest(buf+len, auth, DIGEST_LEN+9)) + if (crypto_digest(buf+len, auth, DIGEST_LEN+9) < 0) goto err; len += 20; note_crypto_pk_op(REND_SERVER); diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 630191e8b7..3b185672f6 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -119,7 +119,16 @@ typedef struct rend_service_t { STATIC void rend_service_free(rend_service_t *service); STATIC char *rend_service_sos_poison_path(const rend_service_t *service); - +STATIC int rend_service_check_dir_and_add(smartlist_t *service_list, + const or_options_t *options, + rend_service_t *service, + int validate_only); +STATIC int rend_service_verify_single_onion_poison( + const rend_service_t *s, + const or_options_t *options); +STATIC int rend_service_poison_new_single_onion_dir( + const rend_service_t *s, + const or_options_t* options); #endif int num_rend_services(void); @@ -165,11 +174,6 @@ 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 */ diff --git a/src/or/router.c b/src/or/router.c index bc0eb3a34a..917caaa1f5 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1968,23 +1968,34 @@ static int router_guess_address_from_dir_headers(uint32_t *guess); /** Make a current best guess at our address, either because * it's configured in torrc, or because we've learned it from * dirserver headers. Place the answer in *<b>addr</b> and return - * 0 on success, else return -1 if we have no guess. */ + * 0 on success, else return -1 if we have no guess. + * + * If <b>cache_only</b> is true, just return any cached answers, and + * don't try to get any new answers. + */ MOCK_IMPL(int, -router_pick_published_address,(const or_options_t *options, uint32_t *addr)) +router_pick_published_address,(const or_options_t *options, uint32_t *addr, + int cache_only)) { + /* First, check the cached output from resolve_my_address(). */ *addr = get_last_resolved_addr(); - if (!*addr && - resolve_my_address(LOG_INFO, options, addr, NULL, NULL) < 0) { - log_info(LD_CONFIG, "Could not determine our address locally. " - "Checking if directory headers provide any hints."); - if (router_guess_address_from_dir_headers(addr) < 0) { - log_info(LD_CONFIG, "No hints from directory headers either. " - "Will try again later."); - return -1; + if (*addr) + return 0; + + /* Second, consider doing a resolve attempt right here. */ + if (!cache_only) { + if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) { + log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); + return 0; } } - log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); - return 0; + + /* Third, check the cached output from router_new_address_suggestion(). */ + if (router_guess_address_from_dir_headers(addr) >= 0) + return 0; + + /* We have no useful cached answers. Return failure. */ + return -1; } /* Like router_check_descriptor_address_consistency, but specifically for the @@ -2081,7 +2092,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); - if (router_pick_published_address(options, &addr) < 0) { + if (router_pick_published_address(options, &addr, 0) < 0) { log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); return -1; } @@ -2330,7 +2341,7 @@ router_rebuild_descriptor(int force) if (desc_clean_since && !force) return 0; - if (router_pick_published_address(options, &addr) < 0 || + if (router_pick_published_address(options, &addr, 0) < 0 || router_get_advertised_or_port(options) == 0) { /* Stop trying to rebuild our descriptor every second. We'll * learn that it's time to try again when ip_address_changed() diff --git a/src/or/router.h b/src/or/router.h index 73bfea1faa..c30a0301b7 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -91,7 +91,8 @@ const uint8_t *router_get_my_id_digest(void); int router_extrainfo_digest_is_me(const char *digest); int router_is_me(const routerinfo_t *router); MOCK_DECL(int,router_pick_published_address,(const or_options_t *options, - uint32_t *addr)); + uint32_t *addr, + int cache_only)); int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index ab78dbe61e..51802b15e5 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -742,8 +742,12 @@ load_ed_keys(const or_options_t *options, time_t now) if (need_new_signing_key) { log_notice(LD_OR, "It looks like I need to generate and sign a new " - "medium-term signing key, because %s. To do that, I need to " - "load%s the permanent master identity key.", + "medium-term signing key, because %s. To do that, I " + "need to load%s the permanent master identity key. " + "If the master identity key was not moved or encrypted " + "with a passphrase, this will be done automatically and " + "no further action is required. Otherwise, provide the " + "necessary data using 'tor --keygen' to do it manually.", (NULL == use_signing) ? "I don't have one" : EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" : "you asked me to make one with --keygen", @@ -751,15 +755,19 @@ load_ed_keys(const or_options_t *options, time_t now) } else if (want_new_signing_key && !offline_master) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " - "going to expire soon. To do that, I'm going to have to try to " - "load the permanent master identity key."); + "going to expire soon. To do that, I'm going to have to " + "try to load the permanent master identity key. " + "If the master identity key was not moved or encrypted " + "with a passphrase, this will be done automatically and " + "no further action is required. Otherwise, provide the " + "necessary data using 'tor --keygen' to do it manually."); } else if (want_new_signing_key) { log_notice(LD_OR, "It looks like I should try to generate and sign a " "new medium-term signing key, because the one I have is " "going to expire soon. But OfflineMasterKey is set, so I " - "won't try to load a permanent master identity key is set. " - "You will need to use 'tor --keygen' make a new signing key " - "and certificate."); + "won't try to load a permanent master identity key. You " + "will need to use 'tor --keygen' to make a new signing " + "key and certificate."); } { diff --git a/src/or/routerlist.c b/src/or/routerlist.c index c99d22ed41..9bcca76b63 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -586,7 +586,7 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, "signing key %s", from_store ? "cached" : "downloaded", ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN)); } else { - int adding = directory_caches_unknown_auth_certs(get_options()); + int adding = we_want_to_fetch_unknown_auth_certs(get_options()); log_info(LD_DIR, "%s %s certificate for unrecognized directory " "authority with signing key %s", adding ? "Adding" : "Not adding", @@ -1012,7 +1012,7 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, char *resource = NULL; cert_list_t *cl; const or_options_t *options = get_options(); - const int cache = directory_caches_unknown_auth_certs(options); + const int keep_unknown = we_want_to_fetch_unknown_auth_certs(options); fp_pair_t *fp_tmp = NULL; char id_digest_str[2*DIGEST_LEN+1]; char sk_digest_str[2*DIGEST_LEN+1]; @@ -1084,9 +1084,10 @@ authority_certs_fetch_missing(networkstatus_t *status, time_t now, if (!smartlist_len(voter->sigs)) continue; /* This authority never signed this consensus, so don't * go looking for a cert with key digest 0000000000. */ - if (!cache && + if (!keep_unknown && !trusteddirserver_get_by_v3_auth_digest(voter->identity_digest)) - continue; /* We are not a cache, and we don't know this authority.*/ + continue; /* We don't want unknown certs, and we don't know this + * authority.*/ /* * If we don't know *any* cert for this authority, and a download by ID @@ -3895,7 +3896,7 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg, router_describe(router)); *msg = "Router descriptor is not referenced by any network-status."; - /* Only journal this desc if we'll be serving it. */ + /* Only journal this desc if we want to keep old descriptors */ if (!from_cache && should_cache_old_descriptors()) signed_desc_append_to_journal(&router->cache_info, &routerlist->desc_store); @@ -4525,13 +4526,14 @@ router_load_extrainfo_from_string(const char *s, const char *eos, smartlist_free(extrainfo_list); } -/** Return true iff any networkstatus includes a descriptor whose digest - * is that of <b>desc</b>. */ +/** Return true iff the latest ns-flavored consensus includes a descriptor + * whose digest is that of <b>desc</b>. */ static int signed_desc_digest_is_recognized(signed_descriptor_t *desc) { const routerstatus_t *rs; - networkstatus_t *consensus = networkstatus_get_latest_consensus(); + networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor( + FLAV_NS); if (consensus) { rs = networkstatus_vote_find_entry(consensus, desc->identity_digest); @@ -5154,7 +5156,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, ++n_would_reject; continue; /* We would throw it out immediately. */ } - if (!directory_caches_dir_info(options) && + if (!we_want_to_fetch_flavor(options, consensus->flavor) && !client_would_use_router(rs, now, options)) { ++n_wouldnt_use; continue; /* We would never use it ourself. */ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 2cfd3fc58a..38ceb942a9 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -715,7 +715,7 @@ dump_desc_populate_one_file, (const char *dirname, const char *f)) * filename. */ if (crypto_digest256((char *)content_digest, desc, (size_t) st.st_size, - DIGEST_SHA256) != 0) { + DIGEST_SHA256) < 0) { /* Weird, but okay */ log_info(LD_DIR, "Unable to hash content of %s from unparseable descriptors " @@ -879,7 +879,7 @@ dump_desc(const char *desc, const char *type) /* Get the hash for logging purposes anyway */ len = strlen(desc); if (crypto_digest256((char *)digest_sha256, desc, len, - DIGEST_SHA256) != 0) { + DIGEST_SHA256) < 0) { log_info(LD_DIR, "Unable to parse descriptor of type %s, and unable to even hash" " it!", type); @@ -4536,12 +4536,12 @@ router_get_hash_impl(const char *s, size_t s_len, char *digest, return -1; if (alg == DIGEST_SHA1) { - if (crypto_digest(digest, start, end-start)) { + if (crypto_digest(digest, start, end-start) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } } else { - if (crypto_digest256(digest, start, end-start, alg)) { + if (crypto_digest256(digest, start, end-start, alg) < 0) { log_warn(LD_BUG,"couldn't compute digest"); return -1; } diff --git a/src/or/shared_random.c b/src/or/shared_random.c index 5f6b03f1ba..0eb93382ca 100644 --- a/src/or/shared_random.c +++ b/src/or/shared_random.c @@ -192,7 +192,7 @@ verify_commit_and_reveal(const sr_commit_t *commit) /* Use the invariant length since the encoded reveal variable has an * extra byte for the NUL terminated byte. */ if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal, - SR_REVEAL_BASE64_LEN, commit->alg)) { + SR_REVEAL_BASE64_LEN, commit->alg) < 0) { /* Unable to digest the reveal blob, this is unlikely. */ goto invalid; } @@ -932,7 +932,7 @@ sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert) /* The invariant length is used here since the encoded reveal variable * has an extra byte added for the NULL terminated byte. */ if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal, - SR_REVEAL_BASE64_LEN, commit->alg)) { + SR_REVEAL_BASE64_LEN, commit->alg) < 0) { goto error; } @@ -1012,7 +1012,7 @@ sr_compute_srv(void) SMARTLIST_FOREACH(chunks, char *, s, tor_free(s)); smartlist_free(chunks); if (crypto_digest256(hashed_reveals, reveals, strlen(reveals), - SR_DIGEST_ALG)) { + SR_DIGEST_ALG) < 0) { goto end; } current_srv = generate_srv(hashed_reveals, reveal_num, diff --git a/src/or/torcert.c b/src/or/torcert.c index 852def9ef6..c58f3da2d3 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -156,11 +156,12 @@ tor_cert_parse(const uint8_t *encoded, const size_t len) cert->encoded_len = len; memcpy(cert->signed_key.pubkey, parsed->certified_key, 32); - const int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600; + int64_t valid_until_64 = ((int64_t)parsed->exp_field) * 3600; +#if SIZEOF_TIME_T < SIZEOF_INT64_T if (valid_until_64 > TIME_MAX) - cert->valid_until = TIME_MAX - 1; - else - cert->valid_until = (time_t) valid_until_64; + valid_until_64 = TIME_MAX - 1; +#endif + 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) { @@ -647,3 +648,44 @@ or_handshake_certs_check_both(int severity, } } +/* === ENCODING === */ + +/* Encode the ed25519 certificate <b>cert</b> and put the newly allocated + * string in <b>cert_str_out</b>. Return 0 on success else a negative value. */ +int +tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out) +{ + int ret = -1; + char *ed_cert_b64 = NULL; + size_t ed_cert_b64_len; + + tor_assert(cert); + tor_assert(cert_str_out); + + /* Get the encoded size and add the NUL byte. */ + ed_cert_b64_len = base64_encode_size(cert->encoded_len, + BASE64_ENCODE_MULTILINE) + 1; + ed_cert_b64 = tor_malloc_zero(ed_cert_b64_len); + + /* Base64 encode the encoded certificate. */ + if (base64_encode(ed_cert_b64, ed_cert_b64_len, + (const char *) cert->encoded, cert->encoded_len, + BASE64_ENCODE_MULTILINE) < 0) { + log_err(LD_BUG, "Couldn't base64-encode ed22519 cert!"); + goto err; + } + + /* Put everything together in a NUL terminated string. */ + tor_asprintf(cert_str_out, + "-----BEGIN ED25519 CERT-----\n" + "%s" + "-----END ED25519 CERT-----", + ed_cert_b64); + /* Success! */ + ret = 0; + + err: + tor_free(ed_cert_b64); + return ret; +} + diff --git a/src/or/torcert.h b/src/or/torcert.h index 4bd816f4a4..090f6b5811 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -98,5 +98,7 @@ void or_handshake_certs_check_both(int severity, const ed25519_public_key_t **ed_id_out, const common_digests_t **rsa_id_out); +int tor_cert_encode_ed22519(const tor_cert_t *cert, char **cert_str_out); + #endif diff --git a/src/or/transports.c b/src/or/transports.c index 614b28c168..f755882c16 100644 --- a/src/or/transports.c +++ b/src/or/transports.c @@ -1611,7 +1611,7 @@ pt_get_extra_info_descriptor_string(void) uint32_t external_ip_address = 0; if (tor_addr_is_null(&t->addr) && router_pick_published_address(get_options(), - &external_ip_address) >= 0) { + &external_ip_address, 0) >= 0) { tor_addr_t addr; tor_addr_from_ipv4h(&addr, external_ip_address); addrport = fmt_addrport(&addr, t->port); diff --git a/src/test/include.am b/src/test/include.am index f6e43148f0..8d0fc2ff6b 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -80,6 +80,7 @@ src_test_test_SOURCES = \ src/test/test_checkdir.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ + src/test/test_circuituse.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ diff --git a/src/test/log_test_helpers.h b/src/test/log_test_helpers.h index 4c020c7ec3..922c68b42f 100644 --- a/src/test/log_test_helpers.h +++ b/src/test/log_test_helpers.h @@ -49,7 +49,22 @@ void mock_dump_saved_logs(void); #define expect_log_msg_containing_either(str1, str2) \ assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ mock_saved_log_has_message_containing(str2), \ - "expected log to contain " # str1 " or " # str2); + "expected log to contain " # str1 " or " # str2); + +#define expect_log_msg_containing_either3(str1, str2, str3) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3); + +#define expect_log_msg_containing_either4(str1, str2, str3, str4) \ + assert_log_predicate(mock_saved_log_has_message_containing(str1) || \ + mock_saved_log_has_message_containing(str2) || \ + mock_saved_log_has_message_containing(str3) || \ + mock_saved_log_has_message_containing(str4), \ + "expected log to contain " # str1 " or " # str2 \ + " or " # str3 " or " # str4); #define expect_single_log_msg(str) \ do { \ diff --git a/src/test/test.c b/src/test/test.c index b02ecb922d..750d8b00e4 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1190,6 +1190,7 @@ struct testgroup_t testgroups[] = { { "checkdir/", checkdir_tests }, { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, + { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, diff --git a/src/test/test.h b/src/test/test.h index 49b4499594..2fa73592ef 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -185,6 +185,7 @@ extern struct testcase_t channeltls_tests[]; extern struct testcase_t checkdir_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; diff --git a/src/test/test_address.c b/src/test/test_address.c index e52779cb64..0d142ad483 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -808,8 +808,13 @@ test_address_get_if_addrs6_list_internal(void *arg) results = get_interface_address6_list(LOG_ERR, AF_INET6, 1); tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); if (smartlist_len(mock_saved_logs()) == 1) { - expect_log_msg_containing_either("connect() failed", - "unable to create socket"); + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); } teardown_capture_of_logs(); @@ -846,8 +851,13 @@ test_address_get_if_addrs6_list_no_internal(void *arg) results = get_interface_address6_list(LOG_ERR, AF_INET6, 0); tt_int_op(smartlist_len(mock_saved_logs()), OP_LE, 1); if (smartlist_len(mock_saved_logs()) == 1) { - expect_log_msg_containing_either("connect() failed", - "unable to create socket"); + expect_log_msg_containing_either4("connect() failed", + "unable to create socket", + "Address that we determined via UDP " + "socket magic is unsuitable for public " + "comms.", + "getsockname() to determine interface " + "failed"); } teardown_capture_of_logs(); diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c new file mode 100644 index 0000000000..edbc9f6391 --- /dev/null +++ b/src/test/test_circuituse.c @@ -0,0 +1,302 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "circuitbuild.h" +#include "nodelist.h" + +static void +test_circuit_is_available_for_use_ret_false_when_marked_for_close(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->marked_for_close = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_when_timestamp_dirty(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->timestamp_dirty = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_origin(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_origin_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_OR; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_unusable_for_new_conns(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_false_for_onehop_tunnel(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_true_for_clean_circuit(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 0; + + tt_int_op(1, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static int +mock_circuit_all_predicted_ports_handled(time_t now, + int *need_uptime, + int *need_capacity) +{ + (void)now; + + if (need_uptime && need_capacity) + return 0; + return 1; +} + +static consensus_path_type_t +mock_router_have_unknown_consensus_path(void) +{ + return CONSENSUS_PATH_UNKNOWN; +} + +static consensus_path_type_t +mock_router_have_exit_consensus_path(void) +{ + return CONSENSUS_PATH_EXIT; +} + +static void +test_needs_exit_circuits_ret_false_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 0; + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); +} + +static void +test_needs_exit_circuits_ret_false_for_non_exit_consensus_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_exit_circuits_ret_true_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + + time_t now = time(NULL); + tt_int_op(1, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_ret_false_consensus_path_unknown(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(0)); + done: ; +} + +static void +test_needs_circuits_for_build_ret_false_if_num_less_than_max(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(13)); + done: + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_returns_true_when_more_are_needed(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(1, ==, needs_circuits_for_build(0)); + done: + UNMOCK(router_have_consensus_path); +} + +struct testcase_t circuituse_tests[] = { + { "marked", + test_circuit_is_available_for_use_ret_false_when_marked_for_close, + TT_FORK, NULL, NULL + }, + { "timestamp", + test_circuit_is_available_for_use_ret_false_when_timestamp_dirty, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_purpose, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_origin, + TT_FORK, NULL, NULL + }, + { "origin", + test_circuit_is_available_for_use_ret_false_for_non_origin_purpose, + TT_FORK, NULL, NULL + }, + { "clean", + test_circuit_is_available_for_use_ret_false_unusable_for_new_conns, + TT_FORK, NULL, NULL + }, + { "onehop", + test_circuit_is_available_for_use_returns_false_for_onehop_tunnel, + TT_FORK, NULL, NULL + }, + { "clean_circ", + test_circuit_is_available_for_use_returns_true_for_clean_circuit, + TT_FORK, NULL, NULL + }, + { "exit_f", + test_needs_exit_circuits_ret_false_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "exit_t", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "non_exit", + test_needs_exit_circuits_ret_false_for_non_exit_consensus_path, + TT_FORK, NULL, NULL + }, + { "true", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "consensus_path_unknown", + test_needs_circuits_for_build_ret_false_consensus_path_unknown, + TT_FORK, NULL, NULL + }, + { "less_than_max", + test_needs_circuits_for_build_ret_false_if_num_less_than_max, + TT_FORK, NULL, NULL + }, + { "more_needed", + test_needs_circuits_for_build_returns_true_when_more_are_needed, + TT_FORK, NULL, NULL + }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index 384bff410f..2fc37b0bb8 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -3480,10 +3480,12 @@ test_config_default_dir_servers(void *arg) static int mock_router_pick_published_address_result = 0; static int -mock_router_pick_published_address(const or_options_t *options, uint32_t *addr) +mock_router_pick_published_address(const or_options_t *options, + uint32_t *addr, int cache_only) { (void)options; (void)addr; + (void)cache_only; return mock_router_pick_published_address_result; } diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 6f83ceff00..fa0a174813 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -3501,10 +3501,15 @@ test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg) (void)arg; tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); tt_int_op(1, ==, purpose_needs_anonymity(0, 0, NULL)); tt_int_op(1, ==, smartlist_len(tor_get_captured_bug_log_())); + expect_single_log_msg_containing("Called with dir_purpose=0"); + tor_end_capture_bugs_(); - done: ; + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); } static void diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 130ec43a3a..132af39776 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -10,6 +10,7 @@ #include "orconfig.h" #include "or.h" +#include "relay.h" #include "routerlist.h" #include "nodelist.h" #include "buffers.h" @@ -23,6 +24,8 @@ DISABLE_GCC_WARNING(overlength-strings) * at large. */ #endif #include "test_descriptors.inc" +#include "or.h" +#include "circuitlist.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS ENABLE_GCC_WARNING(overlength-strings) #endif @@ -105,3 +108,23 @@ connection_write_to_buf_mock(const char *string, size_t len, write_to_buf(string, len, conn->outbuf); } +/* Set up a fake origin circuit with the specified number of cells, + * Return a pointer to the newly-created dummy circuit */ +circuit_t * +dummy_origin_circuit_new(int n_cells) +{ + origin_circuit_t *circ = origin_circuit_new(); + int i; + cell_t cell; + + for (i=0; i < n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + return TO_CIRCUIT(circ); +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index b77a459256..ba93b100d5 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -6,6 +6,8 @@ const char *get_yesterday_date_str(void); +circuit_t * dummy_origin_circuit_new(int num_cells); + /* Number of descriptors contained in test_descriptors.txt. */ #define HELPER_NUMBER_OF_DESCRIPTORS 8 diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 318c549762..6fadeeead2 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -550,17 +550,18 @@ test_single_onion_poisoning(void *arg) rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t)); char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2")); smartlist_t *services = smartlist_new(); + char *poison_path = NULL; - /* No services, no problem! */ + /* No services, no service to verify, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_config_services(mock_options, 1); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_config_services(mock_options, 1); tt_assert(ret == 0); /* Create the data directory, and, if the correct bit in arg is set, @@ -578,44 +579,88 @@ test_single_onion_poisoning(void *arg) tt_assert(ret == 0); } - service_1->directory = dir1; - service_2->directory = dir2; + service_1->directory = tor_strdup(dir1); + service_2->directory = tor_strdup(dir2); + /* The services own the directory pointers now */ dir1 = dir2 = NULL; - smartlist_add(services, service_1); + /* Add port to service 1 */ + service_1->ports = smartlist_new(); + service_2->ports = smartlist_new(); + char *err_msg = NULL; + rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ", + &err_msg); + tt_assert(port1); + tt_assert(!err_msg); + smartlist_add(service_1->ports, port1); + + rend_service_port_config_t *port2 = rend_service_parse_port_config("90", " ", + &err_msg); + /* Add port to service 2 */ + tt_assert(port2); + tt_assert(!err_msg); + smartlist_add(service_2->ports, port2); + + /* No services, a service to verify, no problem! */ + mock_options->HiddenServiceSingleHopMode = 0; + mock_options->HiddenServiceNonAnonymousMode = 0; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Either way, no problem. */ + mock_options->HiddenServiceSingleHopMode = 1; + mock_options->HiddenServiceNonAnonymousMode = 1; + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); + + /* Add the first service */ + ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0); + tt_assert(ret == 0); /* But don't add the second service yet. */ /* Service directories, but no previous keys, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Poison! Poison! Poison! * This can only be done in HiddenServiceSingleHopMode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* Poisoning twice is a no-op. */ - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* Poisoned service directories, but no previous keys, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now add some keys, and we'll have a problem. */ @@ -625,95 +670,121 @@ test_single_onion_poisoning(void *arg) /* Poisoned service directories with previous keys are not allowed. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* But they are allowed if we're in non-anonymous mode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Re-poisoning directories with existing keys is a no-op, because * directories with existing keys are ignored. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); tt_assert(ret == 0); /* And it keeps the poison. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now add the second service: it has no key and no poison file */ - smartlist_add(services, service_2); + ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0); + tt_assert(ret == 0); /* A new service, and an existing poisoned service. Not ok. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* But ok to add in non-anonymous mode. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Now remove the poisoning from the first service, and we have the opposite * problem. */ - char *poison_path = rend_service_sos_poison_path(service_1); + poison_path = rend_service_sos_poison_path(service_1); + tt_assert(poison_path); ret = unlink(poison_path); - tor_free(poison_path); tt_assert(ret == 0); /* Unpoisoned service directories with previous keys are ok, as are empty * directories. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* But the existing unpoisoned key is not ok in non-anonymous mode, even if * there is an empty service. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* Poisoning directories with existing keys is a no-op, because directories * with existing keys are ignored. But the new directory should poison. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); tt_assert(ret == 0); /* And the old directory remains unpoisoned. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); /* And the new directory should be ignored, because it has no key. */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); tt_assert(ret == 0); /* Re-poisoning directories without existing keys is a no-op. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_service_poison_new_single_onion_dirs(services); + ret = rend_service_poison_new_single_onion_dir(service_1, mock_options); + tt_assert(ret == 0); + ret = rend_service_poison_new_single_onion_dir(service_2, mock_options); tt_assert(ret == 0); /* And the old directory remains unpoisoned. */ - ret = rend_service_list_verify_single_onion_poison(services, mock_options); + ret = rend_service_verify_single_onion_poison(service_1, mock_options); tt_assert(ret < 0); + ret = rend_service_verify_single_onion_poison(service_2, mock_options); + tt_assert(ret == 0); done: /* The test harness deletes the directories at exit */ + tor_free(poison_path); + tor_free(dir1); + tor_free(dir2); + smartlist_free(services); rend_service_free(service_1); rend_service_free(service_2); - smartlist_free(services); UNMOCK(get_options); tor_free(mock_options->DataDirectory); - tor_free(dir1); - tor_free(dir2); } struct testcase_t hs_tests[] = { diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index 8af5cabca3..9749c3b096 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -254,7 +254,7 @@ test_cert_encoding(void *arg) tt_assert(cert); /* Test the certificate encoding function. */ - ret = encode_cert(cert, &encoded); + ret = tor_cert_encode_ed22519(cert, &encoded); tt_int_op(ret, ==, 0); /* Validated the certificate string. */ diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 6102af01f5..0f97972032 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -15,6 +15,7 @@ #include "config.h" #include "relay.h" #include "test.h" +#include "test_helpers.h" /* small replacement mock for circuit_mark_for_close_ to avoid doing all * the other bookkeeping that comes with marking circuits. */ @@ -58,24 +59,6 @@ dummy_or_circuit_new(int n_p_cells, int n_n_cells) return TO_CIRCUIT(circ); } -static circuit_t * -dummy_origin_circuit_new(int n_cells) -{ - origin_circuit_t *circ = origin_circuit_new(); - int i; - cell_t cell; - - for (i=0; i < n_cells; ++i) { - crypto_rand((void*)&cell, sizeof(cell)); - cell_queue_append_packed_copy(TO_CIRCUIT(circ), - &TO_CIRCUIT(circ)->n_chan_cells, - 1, &cell, 1, 0); - } - - TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - return TO_CIRCUIT(circ); -} - static void add_bytes_to_buf(buf_t *buf, size_t n_bytes) { diff --git a/src/test/test_options.c b/src/test/test_options.c index 0eada98cb2..e85e11805b 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -1050,7 +1050,7 @@ test_options_validate__transproxy(void *ignored) tt_int_op(ret, OP_EQ, -1); #ifndef KERNEL_MAY_SUPPORT_IPFW - tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specificand OS X/Darwin-specific " + tt_str_op(msg, OP_EQ, "ipfw is a FreeBSD-specific and OS X/Darwin-specific " "feature."); #else tt_int_op(tdata->opt->TransProxyType_parsed, OP_EQ, TPT_IPFW); @@ -1071,26 +1071,38 @@ test_options_validate__transproxy(void *ignored) free_options_test_data(tdata); tdata = NULL; -#if defined(linux) +#if defined(__linux__) tdata = get_options_test_data("TransProxyType tproxy\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__FreeBSD_kernel__) || defined( DARWIN ) || defined(__NetBSD__) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(KERNEL_MAY_SUPPORT_IPFW) tdata = get_options_test_data("TransProxyType ipfw\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); -#endif -#if defined(__OpenBSD__) + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(__OpenBSD__) tdata = get_options_test_data("TransProxyType pf-divert\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); - tt_assert(!msg); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } +#elif defined(__NetBSD__) + tdata = get_options_test_data("TransProxyType default\n" + "TransPort 127.0.0.1:123\n"); + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + if (msg) { + TT_DIE(("Expected NULL but got '%s'", msg)); + } #endif // Assert that a test has run for some TransProxyType diff --git a/src/test/test_util.c b/src/test/test_util.c index b74f658146..7e5a44cb59 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -5531,9 +5531,9 @@ test_util_monotonic_time(void *arg) tt_u64_op(msecc1, OP_GE, nsecc1 / 1000000); tt_u64_op(usecc1, OP_GE, nsecc1 / 1000); tt_u64_op(msec1, OP_LE, nsec1 / 1000000 + 1); - tt_u64_op(usec1, OP_LE, nsec1 / 1000 +10); + tt_u64_op(usec1, OP_LE, nsec1 / 1000 + 1000); tt_u64_op(msecc1, OP_LE, nsecc1 / 1000000 + 1); - tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 10); + tt_u64_op(usecc1, OP_LE, nsecc1 / 1000 + 1000); done: ; |