diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/common/compat.c | 5 | ||||
-rw-r--r-- | src/common/util.c | 12 | ||||
-rw-r--r-- | src/config/torrc.minimal.in-staging | 2 | ||||
-rw-r--r-- | src/config/torrc.sample.in | 2 | ||||
-rw-r--r-- | src/or/circuituse.c | 18 | ||||
-rw-r--r-- | src/or/config.c | 4 | ||||
-rw-r--r-- | src/or/confparse.c | 2 | ||||
-rw-r--r-- | src/or/directory.c | 35 | ||||
-rw-r--r-- | src/or/hs_common.c | 13 | ||||
-rw-r--r-- | src/or/hs_descriptor.c | 13 | ||||
-rw-r--r-- | src/or/hs_descriptor.h | 3 | ||||
-rw-r--r-- | src/or/or.h | 4 | ||||
-rw-r--r-- | src/or/parsecommon.h | 1 | ||||
-rw-r--r-- | src/or/policies.c | 37 | ||||
-rw-r--r-- | src/or/policies.h | 3 | ||||
-rw-r--r-- | src/or/protover.c | 2 | ||||
-rw-r--r-- | src/or/rendservice.c | 76 | ||||
-rw-r--r-- | src/or/router.c | 4 | ||||
-rw-r--r-- | src/or/routerparse.c | 4 | ||||
-rw-r--r-- | src/test/test.h | 1 | ||||
-rw-r--r-- | src/test/test_dir.c | 24 | ||||
-rw-r--r-- | src/test/test_hs.c | 55 | ||||
-rw-r--r-- | src/test/test_hs_descriptor.c | 1 | ||||
-rw-r--r-- | src/test/test_policy.c | 18 | ||||
-rw-r--r-- | src/test/testing_common.c | 34 |
25 files changed, 253 insertions, 120 deletions
diff --git a/src/common/compat.c b/src/common/compat.c index 4f2f9778f2..8d6a491c42 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -532,7 +532,10 @@ tor_vasprintf(char **strp, const char *fmt, va_list args) /* On Windows, _vsnprintf won't tell us the length of the string if it * overflows, so we need to use _vcsprintf to tell how much to allocate */ int len, r; - len = _vscprintf(fmt, args); + va_list tmp_args; + va_copy(tmp_args, args); + len = _vscprintf(fmt, tmp_args); + va_end(tmp_args); if (len < 0) { *strp = NULL; return -1; diff --git a/src/common/util.c b/src/common/util.c index b1159e5334..cb5f12821e 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -2191,11 +2191,13 @@ file_status(const char *fname) } } -/** Check whether <b>dirname</b> exists and is private. If yes return 0. If - * it does not exist, and <b>check</b>&CPD_CREATE is set, try to create it - * and return 0 on success. If it does not exist, and - * <b>check</b>&CPD_CHECK, and we think we can create it, return 0. Else - * return -1. If CPD_GROUP_OK is set, then it's okay if the directory +/** Check whether <b>dirname</b> exists and is private. If yes return 0. + * If <b>dirname</b> does not exist: + * - if <b>check</b>&CPD_CREATE, try to create it and return 0 on success. + * - if <b>check</b>&CPD_CHECK, and we think we can create it, return 0. + * - if <b>check</b>&CPD_CHECK is false, and the directory exists, return 0. + * - otherwise, return -1. + * If CPD_GROUP_OK is set, then it's okay if the directory * is group-readable, but in all cases we create the directory mode 0700. * If CPD_GROUP_READ is set, existing directory behaves as CPD_GROUP_OK and * if the directory is created it will use mode 0750 with group read diff --git a/src/config/torrc.minimal.in-staging b/src/config/torrc.minimal.in-staging index 5fa37c18f0..c537c51f9b 100644 --- a/src/config/torrc.minimal.in-staging +++ b/src/config/torrc.minimal.in-staging @@ -105,7 +105,7 @@ ## Define these to limit how much relayed traffic you will allow. Your ## own traffic is still unthrottled. Note that RelayBandwidthRate must -## be at least 20 kilobytes per second. +## be at least 75 kilobytes per second. ## Note that units for these config options are bytes (per second), not ## bits (per second), and that prefixes are binary prefixes, i.e. 2^10, ## 2^20, etc. diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in index d4dfd5f6bb..5328206da9 100644 --- a/src/config/torrc.sample.in +++ b/src/config/torrc.sample.in @@ -104,7 +104,7 @@ ## Define these to limit how much relayed traffic you will allow. Your ## own traffic is still unthrottled. Note that RelayBandwidthRate must -## be at least 20 kilobytes per second. +## be at least 75 kilobytes per second. ## Note that units for these config options are bytes (per second), not ## bits (per second), and that prefixes are binary prefixes, i.e. 2^10, ## 2^20, etc. diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 71615bc17a..ba7b75ff25 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -807,6 +807,8 @@ static time_t last_expired_clientside_circuits = 0; * As a diagnostic for bug 8387, log information about how many one-hop * circuits we have around that have been there for at least <b>age</b> * seconds. Log a few of them. + * Ignores Single Onion Service intro and Tor2web redezvous circuits, they are + * expected to be long-term one-hop circuits. */ void circuit_log_ancient_one_hop_circuits(int age) @@ -816,6 +818,7 @@ circuit_log_ancient_one_hop_circuits(int age) time_t cutoff = now - age; int n_found = 0; smartlist_t *log_these = smartlist_new(); + const or_options_t *options = get_options(); SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) { const origin_circuit_t *ocirc; @@ -823,6 +826,19 @@ circuit_log_ancient_one_hop_circuits(int age) continue; if (circ->timestamp_created.tv_sec >= cutoff) continue; + /* Single Onion Services deliberately make long term one-hop intro + * connections. We only ignore active intro point connections, if we take + * a long time establishing, that's worth logging. */ + if (rend_service_allow_non_anonymous_connection(options) && + circ->purpose == CIRCUIT_PURPOSE_S_INTRO) + continue; + /* Tor2web deliberately makes long term one-hop rend connections, + * particularly when Tor2webRendezvousPoints is used. We only ignore + * active rend point connections, if we take a long time to rendezvous, + * that's worth logging. */ + if (rend_client_allow_non_anonymous_connection(options) && + circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) + continue; ocirc = CONST_TO_ORIGIN_CIRCUIT(circ); if (ocirc->build_state && ocirc->build_state->onehop_tunnel) { @@ -858,7 +874,7 @@ circuit_log_ancient_one_hop_circuits(int age) tor_asprintf(&dirty, "Dirty since %s (%ld seconds vs %ld-second cutoff)", dirty_since, (long)(now - circ->timestamp_dirty), - (long) get_options()->MaxCircuitDirtiness); + (long) options->MaxCircuitDirtiness); } else { dirty = tor_strdup("Not marked dirty"); } diff --git a/src/or/config.c b/src/or/config.c index fef12083e0..9553822ba3 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -550,7 +550,7 @@ static config_var_t option_vars_[] = { * When clients have authorities and fallbacks available, they use these * schedules: (we stagger the times to avoid thundering herds) */ V(ClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL, - "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), + "6, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */), V(ClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL, "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"), /* When clients only have authorities available, they use this schedule: */ @@ -561,7 +561,7 @@ static config_var_t option_vars_[] = { * blackholed. Clients will try 3 directories simultaneously. * (Relays never use simultaneous connections.) */ V(ClientBootstrapConsensusMaxInProgressTries, UINT, "3"), - V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"), + V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "1200, 900, 900, 3600"), V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"), V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"), V(TestingConsensusMaxDownloadTries, UINT, "8"), diff --git a/src/or/confparse.c b/src/or/confparse.c index ca54284dba..1706fa85e2 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -1224,6 +1224,8 @@ static struct unit_table_t memory_units[] = { { "gbits", 1<<27 }, { "gbit", 1<<27 }, { "tb", U64_LITERAL(1)<<40 }, + { "tbyte", U64_LITERAL(1)<<40 }, + { "tbytes", U64_LITERAL(1)<<40 }, { "terabyte", U64_LITERAL(1)<<40 }, { "terabytes", U64_LITERAL(1)<<40 }, { "terabits", U64_LITERAL(1)<<37 }, diff --git a/src/or/directory.c b/src/or/directory.c index 8c7953020d..65ddd7d583 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3997,14 +3997,26 @@ next_random_exponential_delay(int delay, int max_delay) /* How much are we willing to add to the delay? */ int max_increment; + int multiplier = 3; /* no more than quadruple the previous delay */ + if (get_options()->TestingTorNetwork) { + /* Decrease the multiplier in testing networks. This reduces the variance, + * so that bootstrap is more reliable. */ + multiplier = 2; /* no more than triple the previous delay */ + } - if (delay) - max_increment = delay; /* no more than double. */ - else - max_increment = 1; /* we're always willing to slow down a little. */ + if (delay && delay < (INT_MAX-1) / multiplier) { + max_increment = delay * multiplier; + } else if (delay) { + max_increment = INT_MAX-1; + } else { + max_increment = 1; + } + + if (BUG(max_increment < 1)) + max_increment = 1; - /* the + 1 here is so that we include the end of the interval */ - int increment = crypto_rand_int(max_increment+1); + /* the + 1 here is so that we always wait longer than last time. */ + int increment = crypto_rand_int(max_increment)+1; if (increment < max_delay - delay) return delay + increment; @@ -4048,7 +4060,7 @@ download_status_schedule_get_delay(download_status_t *dls, delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1); } else if (dls->backoff == DL_SCHED_RANDOM_EXPONENTIAL) { /* Check if we missed a reset somehow */ - if (dls->last_backoff_position > dls_schedule_position) { + IF_BUG_ONCE(dls->last_backoff_position > dls_schedule_position) { dls->last_backoff_position = 0; dls->last_delay_used = 0; } @@ -4134,15 +4146,16 @@ time_t download_status_increment_failure(download_status_t *dls, int status_code, const char *item, int server, time_t now) { + (void) status_code; // XXXX no longer used. + (void) server; // XXXX no longer used. int increment = -1; int min_delay = 0, max_delay = INT_MAX; tor_assert(dls); - /* only count the failure if it's permanent, or we're a server */ - if (status_code != 503 || server) { - if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) - ++dls->n_download_failures; + /* count the failure */ + if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1) { + ++dls->n_download_failures; } if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) { diff --git a/src/or/hs_common.c b/src/or/hs_common.c index b7ee888842..7dd97e7c7c 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -135,7 +135,9 @@ compute_desc_id(rend_data_t *rend_data) /* Allocate and initialize a rend_data_t object for a service using the * provided arguments. All arguments are optional (can be NULL), except from - * <b>onion_address</b> which MUST be set. + * <b>onion_address</b> which MUST be set. The <b>pk_digest</b> is the hash of + * the service private key. The <b>cookie</b> is the rendezvous cookie and + * <b>auth_type</b> is which authentiation this service is configured with. * * Return a valid rend_data_t pointer. This only returns a version 2 object of * rend_data_t. */ @@ -163,10 +165,11 @@ rend_data_service_create(const char *onion_address, const char *pk_digest, return rend_data; } -/* Allocate and initialize a rend_data_t object for a client request using - * the given arguments. Either an onion address or a descriptor ID is - * needed. Both can be given but only the onion address will be used to make - * the descriptor fetch. +/* Allocate and initialize a rend_data_t object for a client request using the + * given arguments. Either an onion address or a descriptor ID is needed. Both + * can be given but in this case only the onion address will be used to make + * the descriptor fetch. The <b>cookie</b> is the rendezvous cookie and + * <b>auth_type</b> is which authentiation the service is configured with. * * Return a valid rend_data_t pointer or NULL on error meaning the * descriptor IDs couldn't be computed from the given data. */ diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c index 96db936735..1517ccb12e 100644 --- a/src/or/hs_descriptor.c +++ b/src/or/hs_descriptor.c @@ -26,6 +26,7 @@ /* Constant string value for the encrypted part of the descriptor. */ #define str_create2_formats "create2-formats" #define str_auth_required "authentication-required" +#define str_single_onion "single-onion-service" #define str_intro_point "introduction-point" #define str_ip_auth_key "auth-key" #define str_ip_enc_key "enc-key" @@ -63,6 +64,7 @@ static token_rule_t hs_desc_v3_token_table[] = { static token_rule_t hs_desc_encrypted_v3_token_table[] = { T1_START(str_create2_formats, R3_CREATE2_FORMATS, CONCAT_ARGS, NO_OBJ), T01(str_auth_required, R3_AUTHENTICATION_REQUIRED, ARGS, NO_OBJ), + T01(str_single_onion, R3_SINGLE_ONION_SERVICE, ARGS, NO_OBJ), END_OF_TABLE }; @@ -692,6 +694,10 @@ encode_encrypted_data(const hs_descriptor_t *desc, smartlist_add_asprintf(lines, "%s %s\n", str_auth_required, buf); tor_free(buf); } + + if (desc->encrypted_data.single_onion_service) { + smartlist_add_asprintf(lines, "%s\n", str_single_onion); + } } /* Build the introduction point(s) section. */ @@ -1613,6 +1619,13 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc, goto err; } } + + /* Is this service a single onion service? */ + tok = find_opt_by_keyword(tokens, R3_SINGLE_ONION_SERVICE); + if (tok) { + desc_encrypted_out->single_onion_service = 1; + } + /* Initialize the descriptor's introduction point list before we start * decoding. Having 0 intro point is valid. Then decode them all. */ desc_encrypted_out->intro_points = smartlist_new(); diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h index 8bc725674e..895bed2485 100644 --- a/src/or/hs_descriptor.h +++ b/src/or/hs_descriptor.h @@ -128,6 +128,9 @@ typedef struct hs_desc_encrypted_data_t { * in order to contact the service. Contains NULL terminated strings. */ smartlist_t *auth_types; + /* Is this descriptor a single onion service? */ + unsigned int single_onion_service : 1; + /* A list of intro points. Contains hs_desc_intro_point_t objects. */ smartlist_t *intro_points; } hs_desc_encrypted_data_t; diff --git a/src/or/or.h b/src/or/or.h index a43e2a33cd..eb94f63d5e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2265,6 +2265,10 @@ typedef struct routerstatus_t { * accept EXTEND2 cells */ unsigned int supports_extend2_cells:1; + /** True iff this router has a protocol list that allows it to negotiate + * ed25519 identity keys on a link handshake. */ + unsigned int supports_ed25519_link_handshake:1; + unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */ unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */ unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with diff --git a/src/or/parsecommon.h b/src/or/parsecommon.h index 3a86c52f3c..3019df63eb 100644 --- a/src/or/parsecommon.h +++ b/src/or/parsecommon.h @@ -158,6 +158,7 @@ typedef enum { R3_SIGNATURE, R3_CREATE2_FORMATS, R3_AUTHENTICATION_REQUIRED, + R3_SINGLE_ONION_SERVICE, R3_INTRODUCTION_POINT, R3_INTRO_AUTH_KEY, R3_INTRO_ENC_KEY, diff --git a/src/or/policies.c b/src/or/policies.c index 9e4e73dfea..f4c0cddbcc 100644 --- a/src/or/policies.c +++ b/src/or/policies.c @@ -281,28 +281,22 @@ parse_reachable_addresses(void) /* We ignore ReachableAddresses for relays */ if (!server_mode(options)) { - if ((reachable_or_addr_policy - && policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC)) - || (reachable_dir_addr_policy - && policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC))) { + if (policy_is_reject_star(reachable_or_addr_policy, AF_UNSPEC, 0) + || policy_is_reject_star(reachable_dir_addr_policy, AF_UNSPEC,0)) { log_warn(LD_CONFIG, "Tor cannot connect to the Internet if " "ReachableAddresses, ReachableORAddresses, or " "ReachableDirAddresses reject all addresses. Please accept " "some addresses in these options."); } else if (options->ClientUseIPv4 == 1 - && ((reachable_or_addr_policy - && policy_is_reject_star(reachable_or_addr_policy, AF_INET)) - || (reachable_dir_addr_policy - && policy_is_reject_star(reachable_dir_addr_policy, AF_INET)))) { + && (policy_is_reject_star(reachable_or_addr_policy, AF_INET, 0) + || policy_is_reject_star(reachable_dir_addr_policy, AF_INET, 0))) { log_warn(LD_CONFIG, "You have set ClientUseIPv4 1, but " "ReachableAddresses, ReachableORAddresses, or " "ReachableDirAddresses reject all IPv4 addresses. " "Tor will not connect using IPv4."); } else if (fascist_firewall_use_ipv6(options) - && ((reachable_or_addr_policy - && policy_is_reject_star(reachable_or_addr_policy, AF_INET6)) - || (reachable_dir_addr_policy - && policy_is_reject_star(reachable_dir_addr_policy, AF_INET6)))) { + && (policy_is_reject_star(reachable_or_addr_policy, AF_INET6, 0) + || policy_is_reject_star(reachable_dir_addr_policy, AF_INET6, 0))) { log_warn(LD_CONFIG, "You have configured tor to use IPv6 " "(ClientUseIPv6 1 or UseBridges 1), but " "ReachableAddresses, ReachableORAddresses, or " @@ -1091,8 +1085,8 @@ validate_addr_policies(const or_options_t *options, char **msg) const int exitrelay_setting_is_auto = options->ExitRelay == -1; const int policy_accepts_something = - ! (policy_is_reject_star(addr_policy, AF_INET) && - policy_is_reject_star(addr_policy, AF_INET6)); + ! (policy_is_reject_star(addr_policy, AF_INET, 1) && + policy_is_reject_star(addr_policy, AF_INET6, 1)); if (server_mode(options) && ! warned_about_exitrelay && @@ -2163,13 +2157,16 @@ exit_policy_is_general_exit(smartlist_t *policy) } /** Return false if <b>policy</b> might permit access to some addr:port; - * otherwise if we are certain it rejects everything, return true. */ + * otherwise if we are certain it rejects everything, return true. If no + * part of <b>policy</b> matches, return <b>default_reject</b>. + * NULL policies are allowed, and treated as empty. */ int -policy_is_reject_star(const smartlist_t *policy, sa_family_t family) +policy_is_reject_star(const smartlist_t *policy, sa_family_t family, + int default_reject) { - if (!policy) /*XXXX disallow NULL policies? */ - return 1; - SMARTLIST_FOREACH_BEGIN(policy, addr_policy_t *, p) { + if (!policy) + return default_reject; + SMARTLIST_FOREACH_BEGIN(policy, const addr_policy_t *, p) { if (p->policy_type == ADDR_POLICY_ACCEPT && (tor_addr_family(&p->addr) == family || tor_addr_family(&p->addr) == AF_UNSPEC)) { @@ -2182,7 +2179,7 @@ policy_is_reject_star(const smartlist_t *policy, sa_family_t family) return 1; } } SMARTLIST_FOREACH_END(p); - return 1; + return default_reject; } /** Write a single address policy to the buf_len byte buffer at buf. Return diff --git a/src/or/policies.h b/src/or/policies.h index e134e686d2..20f58f2beb 100644 --- a/src/or/policies.h +++ b/src/or/policies.h @@ -100,7 +100,8 @@ void addr_policy_append_reject_addr_list(smartlist_t **dest, const smartlist_t *addrs); void policies_set_node_exitpolicy_to_reject_all(node_t *exitrouter); int exit_policy_is_general_exit(smartlist_t *policy); -int policy_is_reject_star(const smartlist_t *policy, sa_family_t family); +int policy_is_reject_star(const smartlist_t *policy, sa_family_t family, + int reject_by_default); char * policy_dump_to_string(const smartlist_t *policy_list, int include_ipv4, int include_ipv6); diff --git a/src/or/protover.c b/src/or/protover.c index 6c2da165f7..5972e61be7 100644 --- a/src/or/protover.c +++ b/src/or/protover.c @@ -293,7 +293,7 @@ protover_get_supported_protocols(void) "HSIntro=3 " "HSRend=1-2 " "Link=1-4 " - "LinkAuth=1 " + "LinkAuth=1,3 " "Microdesc=1-2 " "Relay=1-2"; } diff --git a/src/or/rendservice.c b/src/or/rendservice.c index b6bf63d829..0819d8a713 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -73,6 +73,10 @@ static ssize_t rend_service_parse_intro_for_v3( size_t plaintext_len, char **err_msg_out); +static int rend_service_check_private_dir(const or_options_t *options, + const rend_service_t *s, + int create); + /** Represents the mapping from a virtual port of a rendezvous service to * a real port on some IP. */ @@ -472,6 +476,11 @@ rend_config_services(const or_options_t *options, int validate_only) 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); + return -1; + } + if (validate_only) rend_service_free(service); else @@ -687,12 +696,7 @@ rend_config_services(const or_options_t *options, int validate_only) } } if (service) { - cpd_check_t check_opts = CPD_CHECK_MODE_ONLY|CPD_CHECK; - if (service->dir_group_readable) { - check_opts |= CPD_GROUP_READ; - } - - if (check_private_dir(service->directory, check_opts, options->User) < 0) { + if (rend_service_check_private_dir(options, service, 0) < 0) { rend_service_free(service); return -1; } @@ -1016,7 +1020,9 @@ service_is_single_onion_poisoned(const rend_service_t *service) fstatus = file_status(poison_fname); tor_free(poison_fname); - /* If this fname is occupied, the hidden service has been poisoned. */ + /* If this fname is occupied, the hidden service has been poisoned. + * fstatus can be FN_ERROR if the service directory does not exist, in that + * case, there is obviously no private key. */ if (fstatus == FN_FILE || fstatus == FN_EMPTY) { return 1; } @@ -1032,7 +1038,9 @@ rend_service_private_key_exists(const rend_service_t *service) char *private_key_path = rend_service_path(service, private_key_fname); const file_status_t private_key_status = file_status(private_key_path); tor_free(private_key_path); - /* Only non-empty regular private key files could have been used before. */ + /* Only non-empty regular private key files could have been used before. + * fstatus can be FN_ERROR if the service directory does not exist, in that + * case, there is obviously no private key. */ return private_key_status == FN_FILE; } @@ -1105,6 +1113,10 @@ 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) + return -1; + poison_fname = rend_service_sos_poison_path(service); switch (file_status(poison_fname)) { @@ -1260,6 +1272,37 @@ rend_service_derive_key_digests(struct rend_service_t *s) 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: + * - if the directory exists, change permissions if needed, + * - if the directory does not exist, create it with the correct permissions. + * If <b>create</b> is false: + * - if the directory exists, check permissions, + * - if the directory does not exist, check if we think we can create it. + * Return 0 on success, -1 on failure. */ +static int +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; + } + /* Check/create directory */ + if (check_private_dir(s->directory, check_opts, options->User) < 0) { + return -1; + } + return 0; +} + /** Load and/or generate private keys for the hidden service <b>s</b>, * possibly including keys for client authorization. Return 0 on success, -1 * on failure. */ @@ -1268,24 +1311,9 @@ rend_service_load_keys(rend_service_t *s) { char *fname = NULL; char buf[128]; - cpd_check_t check_opts = CPD_CREATE; - if (s->dir_group_readable) { - check_opts |= CPD_GROUP_READ; - } - /* Check/create directory */ - if (check_private_dir(s->directory, check_opts, get_options()->User) < 0) { + if (rend_service_check_private_dir(get_options(), s, 1) < 0) goto err; - } -#ifndef _WIN32 - if (s->dir_group_readable) { - /* Only new dirs created get new opts, also enforce group read. */ - if (chmod(s->directory, 0750)) { - log_warn(LD_FS,"Unable to make %s group-readable.", - rend_service_escaped_dir(s)); - } - } -#endif /* Load key */ fname = rend_service_path(s, private_key_fname); diff --git a/src/or/router.c b/src/or/router.c index e45f233634..79caf42a2a 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2158,8 +2158,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) &ri->exit_policy); } ri->policy_is_reject_star = - policy_is_reject_star(ri->exit_policy, AF_INET) && - policy_is_reject_star(ri->exit_policy, AF_INET6); + policy_is_reject_star(ri->exit_policy, AF_INET, 1) && + policy_is_reject_star(ri->exit_policy, AF_INET6, 1); if (options->IPv6Exit) { char *p_tmp = policy_summarize(ri->exit_policy, AF_INET6); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 5bc2d39579..2cfd3fc58a 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -1924,7 +1924,7 @@ router_parse_entry_from_string(const char *s, const char *end, } } - if (policy_is_reject_star(router->exit_policy, AF_INET) && + if (policy_is_reject_star(router->exit_policy, AF_INET, 1) && (!router->ipv6_exit_policy || short_policy_is_reject_star(router->ipv6_exit_policy))) router->policy_is_reject_star = 1; @@ -2682,6 +2682,8 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->protocols_known = 1; rs->supports_extend2_cells = protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2); + rs->supports_ed25519_link_handshake = + protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3); } if ((tok = find_opt_by_keyword(tokens, K_V))) { tor_assert(tok->n_args == 1); diff --git a/src/test/test.h b/src/test/test.h index 15ccf5c379..49b4499594 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -73,6 +73,7 @@ {print_ = (I64_PRINTF_TYPE) value_;}, {}, TT_EXIT_TEST_FUNCTION) const char *get_fname(const char *name); +const char *get_fname_rnd(const char *name); struct crypto_pk_t *pk_generate(int idx); void init_pregenerated_keys(void); void free_pregenerated_keys(void); diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 4281109812..6f83ceff00 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -3935,8 +3935,8 @@ test_dir_download_status_random_backoff(void *arg) tt_int_op(increment, OP_GE, min_delay); tt_int_op(increment, OP_LE, max_delay); tt_int_op(increment, OP_GE, old_increment); - /* We at most double, and maybe add one */ - tt_int_op(increment, OP_LE, 2 * old_increment + 1); + /* We at most quadruple, and maybe add one */ + tt_int_op(increment, OP_LE, 4 * old_increment + 1); /* Advance */ current_time += increment; @@ -4041,14 +4041,14 @@ test_dir_download_status_increment(void *arg) current_time + delay1 + 10, 0) == 0); - /* Check that failure increments don't happen on 503 for clients, but that - * attempt increments do. */ + /* Check that failure increments do happen on 503 for clients, and + * attempt increments do too. */ mock_get_options_calls = 0; next_at = download_status_increment_failure(&dls_failure, 503, "test", 0, current_time); - tt_assert(next_at == current_time + delay1); - tt_assert(download_status_get_n_failures(&dls_failure) == 1); - tt_assert(download_status_get_n_attempts(&dls_failure) == 2); + tt_i64_op(next_at, ==, current_time + delay2); + tt_int_op(download_status_get_n_failures(&dls_failure), ==, 2); + tt_int_op(download_status_get_n_attempts(&dls_failure), ==, 2); tt_assert(mock_get_options_calls >= 1); /* Check that failure increments do happen on 503 for servers */ @@ -4056,7 +4056,7 @@ test_dir_download_status_increment(void *arg) next_at = download_status_increment_failure(&dls_failure, 503, "test", 1, current_time); tt_assert(next_at == current_time + delay2); - tt_assert(download_status_get_n_failures(&dls_failure) == 2); + tt_assert(download_status_get_n_failures(&dls_failure) == 3); tt_assert(download_status_get_n_attempts(&dls_failure) == 3); tt_assert(mock_get_options_calls >= 1); @@ -4065,7 +4065,7 @@ test_dir_download_status_increment(void *arg) next_at = download_status_increment_failure(&dls_failure, 404, "test", 0, current_time); tt_assert(next_at == current_time + delay2); - tt_assert(download_status_get_n_failures(&dls_failure) == 3); + tt_assert(download_status_get_n_failures(&dls_failure) == 4); tt_assert(download_status_get_n_attempts(&dls_failure) == 4); tt_assert(mock_get_options_calls >= 1); @@ -4227,8 +4227,13 @@ test_dir_download_status_increment(void *arg) /* Check that attempt increments don't happen on failure-based schedules, * and that the attempt is set at the end of time */ mock_get_options_calls = 0; + setup_full_capture_of_logs(LOG_WARN); next_at = download_status_increment_attempt(&dls_failure, "test", current_time); + expect_single_log_msg_containing( + "Tried to launch an attempt-based connection on a failure-based " + "schedule."); + teardown_capture_of_logs(); tt_assert(next_at == TIME_MAX); tt_assert(download_status_get_n_failures(&dls_failure) == 0); tt_assert(download_status_get_n_attempts(&dls_failure) == 0); @@ -4240,6 +4245,7 @@ test_dir_download_status_increment(void *arg) UNMOCK(get_options); mock_options = NULL; mock_get_options_calls = 0; + teardown_capture_of_logs(); } static void diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 67ce1cfaed..318c549762 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -527,6 +527,11 @@ mock_get_options(void) return mock_options; } +/* arg can't be 0 (the test fails) or 2 (the test is skipped) */ +#define CREATE_HS_DIR_NONE ((intptr_t)0x04) +#define CREATE_HS_DIR1 ((intptr_t)0x08) +#define CREATE_HS_DIR2 ((intptr_t)0x10) + /* Test that single onion poisoning works. */ static void test_single_onion_poisoning(void *arg) @@ -537,15 +542,15 @@ test_single_onion_poisoning(void *arg) MOCK(get_options, mock_get_options); int ret = -1; - mock_options->DataDirectory = tor_strdup(get_fname("test_data_dir")); + intptr_t create_dir_mask = (intptr_t)arg; + /* Get directories with a random suffix so we can repeat the tests */ + mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir")); rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t)); - char *dir1 = tor_strdup(get_fname("test_hs_dir1")); + char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1")); rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t)); - char *dir2 = tor_strdup(get_fname("test_hs_dir2")); + char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2")); smartlist_t *services = smartlist_new(); - (void) arg; - /* No services, no problem! */ mock_options->HiddenServiceSingleHopMode = 0; mock_options->HiddenServiceNonAnonymousMode = 0; @@ -558,22 +563,20 @@ test_single_onion_poisoning(void *arg) ret = rend_service_list_verify_single_onion_poison(services, mock_options); tt_assert(ret == 0); - /* Create directories for both services */ - -#ifdef _WIN32 - ret = mkdir(mock_options->DataDirectory); - tt_assert(ret == 0); - ret = mkdir(dir1); - tt_assert(ret == 0); - ret = mkdir(dir2); -#else - ret = mkdir(mock_options->DataDirectory, 0700); - tt_assert(ret == 0); - ret = mkdir(dir1, 0700); - tt_assert(ret == 0); - ret = mkdir(dir2, 0700); -#endif + /* Create the data directory, and, if the correct bit in arg is set, + * create a directory for that service. + * The data directory is required for the lockfile, which is used when + * loading keys. */ + ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL); tt_assert(ret == 0); + if (create_dir_mask & CREATE_HS_DIR1) { + ret = check_private_dir(dir1, CPD_CREATE, NULL); + tt_assert(ret == 0); + } + if (create_dir_mask & CREATE_HS_DIR2) { + ret = check_private_dir(dir2, CPD_CREATE, NULL); + tt_assert(ret == 0); + } service_1->directory = dir1; service_2->directory = dir2; @@ -703,7 +706,7 @@ test_single_onion_poisoning(void *arg) tt_assert(ret < 0); done: - /* TODO: should we delete the directories here? */ + /* The test harness deletes the directories at exit */ rend_service_free(service_1); rend_service_free(service_2); smartlist_free(services); @@ -725,8 +728,14 @@ struct testcase_t hs_tests[] = { NULL, NULL }, { "hs_auth_cookies", test_hs_auth_cookies, TT_FORK, NULL, NULL }, - { "single_onion_poisoning", test_single_onion_poisoning, TT_FORK, - NULL, NULL }, + { "single_onion_poisoning_create_dir_none", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR_NONE) }, + { "single_onion_poisoning_create_dir1", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1) }, + { "single_onion_poisoning_create_dir2", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) }, + { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning, + TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) }, END_OF_TESTCASES }; diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c index ea8740bfc8..8af5cabca3 100644 --- a/src/test/test_hs_descriptor.c +++ b/src/test/test_hs_descriptor.c @@ -102,6 +102,7 @@ helper_build_hs_desc(unsigned int no_ip) /* Setup encrypted data section. */ desc->encrypted_data.create2_ntor = 1; desc->encrypted_data.auth_types = smartlist_new(); + desc->encrypted_data.single_onion_service = 1; smartlist_add(desc->encrypted_data.auth_types, tor_strdup("ed25519")); desc->encrypted_data.intro_points = smartlist_new(); if (!no_ip) { diff --git a/src/test/test_policy.c b/src/test/test_policy.c index b89de01b7b..4df40f618e 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -258,14 +258,16 @@ test_policies_general(void *arg) tt_assert(!cmp_addr_policies(policy2, policy2)); tt_assert(!cmp_addr_policies(NULL, NULL)); - tt_assert(!policy_is_reject_star(policy2, AF_INET)); - tt_assert(policy_is_reject_star(policy, AF_INET)); - tt_assert(policy_is_reject_star(policy10, AF_INET)); - tt_assert(!policy_is_reject_star(policy10, AF_INET6)); - tt_assert(policy_is_reject_star(policy11, AF_INET)); - tt_assert(policy_is_reject_star(policy11, AF_INET6)); - tt_assert(policy_is_reject_star(NULL, AF_INET)); - tt_assert(policy_is_reject_star(NULL, AF_INET6)); + tt_assert(!policy_is_reject_star(policy2, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy10, AF_INET, 1)); + tt_assert(!policy_is_reject_star(policy10, AF_INET6, 1)); + tt_assert(policy_is_reject_star(policy11, AF_INET, 1)); + tt_assert(policy_is_reject_star(policy11, AF_INET6, 1)); + tt_assert(policy_is_reject_star(NULL, AF_INET, 1)); + tt_assert(policy_is_reject_star(NULL, AF_INET6, 1)); + tt_assert(!policy_is_reject_star(NULL, AF_INET, 0)); + tt_assert(!policy_is_reject_star(NULL, AF_INET6, 0)); addr_policy_list_free(policy); policy = NULL; diff --git a/src/test/testing_common.c b/src/test/testing_common.c index deb11d0112..caeae13a38 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -102,18 +102,41 @@ setup_directory(void) temp_dir_setup_in_pid = getpid(); } -/** Return a filename relative to our testing temporary directory */ -const char * -get_fname(const char *name) +/** Return a filename relative to our testing temporary directory, based on + * name and suffix. If name is NULL, return the name of the testing temporary + * directory. */ +static const char * +get_fname_suffix(const char *name, const char *suffix) { static char buf[1024]; setup_directory(); if (!name) return temp_dir; - tor_snprintf(buf,sizeof(buf),"%s/%s",temp_dir,name); + tor_snprintf(buf,sizeof(buf),"%s/%s%s%s",temp_dir,name,suffix ? "_" : "", + suffix ? suffix : ""); return buf; } +/** Return a filename relative to our testing temporary directory. If name is + * NULL, return the name of the testing temporary directory. */ +const char * +get_fname(const char *name) +{ + return get_fname_suffix(name, NULL); +} + +/** Return a filename with a random suffix, relative to our testing temporary + * directory. If name is NULL, return the name of the testing temporary + * directory, without any suffix. */ +const char * +get_fname_rnd(const char *name) +{ + char rnd[256], rnd32[256]; + crypto_rand(rnd, RAND_PATH_BYTES); + base32_encode(rnd32, sizeof(rnd32), rnd, RAND_PATH_BYTES); + return get_fname_suffix(name, rnd32); +} + /* Remove a directory and all of its subdirectories */ static void rm_rf(const char *dir) @@ -158,6 +181,9 @@ remove_directory(void) static void * passthrough_test_setup(const struct testcase_t *testcase) { + /* Make sure the passthrough doesn't unintentionally fail or skip tests */ + tor_assert(testcase->setup_data); + tor_assert(testcase->setup_data != (void*)TT_SKIP); return testcase->setup_data; } static int |