aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app/config/config.c2
-rw-r--r--src/app/main/shutdown.c3
-rw-r--r--src/app/main/subsystem_list.c18
-rw-r--r--src/core/crypto/hs_ntor.c44
-rw-r--r--src/core/crypto/hs_ntor.h21
-rw-r--r--src/core/mainloop/connection.c56
-rw-r--r--src/core/mainloop/connection.h4
-rw-r--r--src/core/mainloop/mainloop.c1
-rw-r--r--src/core/or/channeltls.c1
-rw-r--r--src/core/or/circuitbuild.c4
-rw-r--r--src/core/or/circuitbuild.h3
-rw-r--r--src/core/or/connection_edge.c19
-rw-r--r--src/core/or/connection_edge.h15
-rw-r--r--src/core/or/connection_or.c603
-rw-r--r--src/core/or/connection_or.h14
-rw-r--r--src/core/or/include.am2
-rw-r--r--src/core/or/ocirc_event.c11
-rw-r--r--src/core/or/ocirc_event_sys.h13
-rw-r--r--src/core/or/or_sys.c12
-rw-r--r--src/core/or/or_sys.h4
-rw-r--r--src/core/or/orconn_event.c11
-rw-r--r--src/core/or/orconn_event_sys.h12
-rw-r--r--src/core/or/scheduler.c75
-rw-r--r--src/core/or/scheduler_kist.c7
-rw-r--r--src/core/proto/proto_ext_or.h7
-rw-r--r--src/feature/client/addressmap.c3
-rw-r--r--src/feature/client/transports.c6
-rw-r--r--src/feature/control/btrack.c2
-rw-r--r--src/feature/dirauth/dirauth_config.c9
-rw-r--r--src/feature/dirauth/dirauth_config.h4
-rw-r--r--src/feature/dirauth/dirauth_options.inc7
-rw-r--r--src/feature/dirauth/dirvote.c75
-rw-r--r--src/feature/dirauth/dirvote.h7
-rw-r--r--src/feature/dircache/dircache.c15
-rw-r--r--src/feature/dirclient/dirclient.c2
-rw-r--r--src/feature/hs/hs_cell.c138
-rw-r--r--src/feature/hs/hs_cell.h15
-rw-r--r--src/feature/hs/hs_circuit.c57
-rw-r--r--src/feature/hs/hs_circuit.h11
-rw-r--r--src/feature/hs/hs_client.c11
-rw-r--r--src/feature/hs/hs_common.c50
-rw-r--r--src/feature/hs/hs_common.h7
-rw-r--r--src/feature/hs/hs_config.c25
-rw-r--r--src/feature/hs/hs_descriptor.c26
-rw-r--r--src/feature/hs/hs_descriptor.h7
-rw-r--r--src/feature/hs/hs_ob.c408
-rw-r--r--src/feature/hs/hs_ob.h40
-rw-r--r--src/feature/hs/hs_service.c26
-rw-r--r--src/feature/hs/hs_service.h15
-rw-r--r--src/feature/hs/include.am2
-rw-r--r--src/feature/nodelist/dirlist.c31
-rw-r--r--src/feature/nodelist/dirlist.h2
-rw-r--r--src/feature/nodelist/nodelist.c47
-rw-r--r--src/feature/nodelist/nodelist.h6
-rw-r--r--src/feature/relay/dns.c45
-rw-r--r--src/feature/relay/dns.h61
-rw-r--r--src/feature/relay/ext_orport.c71
-rw-r--r--src/feature/relay/ext_orport.h50
-rw-r--r--src/feature/relay/include.am12
-rw-r--r--src/feature/relay/relay_handshake.c565
-rw-r--r--src/feature/relay/relay_handshake.h90
-rw-r--r--src/feature/relay/router.h1
-rw-r--r--src/feature/relay/routerkeys.h82
-rw-r--r--src/feature/relay/selftest.h33
-rw-r--r--src/include.am1
-rw-r--r--src/lib/compress/compress.c2
-rw-r--r--src/lib/ctime/di_ops.c27
-rw-r--r--src/lib/ctime/di_ops.h2
-rw-r--r--src/lib/llharden/.may_include3
-rw-r--r--src/lib/llharden/include.am19
-rw-r--r--src/lib/llharden/lib_llharden.md6
-rw-r--r--src/lib/llharden/winprocess_sys.c (renamed from src/lib/process/winprocess_sys.c)2
-rw-r--r--src/lib/llharden/winprocess_sys.h (renamed from src/lib/process/winprocess_sys.h)0
-rw-r--r--src/lib/net/network_sys.c2
-rw-r--r--src/lib/process/include.am6
-rw-r--r--src/lib/process/process_sys.c2
-rw-r--r--src/lib/thread/compat_threads.c4
-rw-r--r--src/test/fuzz/fuzz_hsdescv3.c7
-rw-r--r--src/test/hs_test_helpers.c51
-rw-r--r--src/test/hs_test_helpers.h15
-rw-r--r--src/test/include.am13
-rw-r--r--src/test/test.c1
-rw-r--r--src/test/test.h1
-rw-r--r--src/test/test_address_set.c15
-rw-r--r--src/test/test_bwmgt.c217
-rw-r--r--src/test/test_dir_handle_get.c203
-rw-r--r--src/test/test_dns.c8
-rw-r--r--src/test/test_hs_cache.c21
-rw-r--r--src/test/test_hs_client.c7
-rw-r--r--src/test/test_hs_common.c6
-rw-r--r--src/test/test_hs_descriptor.c28
-rw-r--r--src/test/test_hs_ntor.c8
-rw-r--r--src/test/test_hs_ntor_cl.c16
-rw-r--r--src/test/test_hs_ob.c268
-rw-r--r--src/test/test_hs_service.c356
-rw-r--r--src/test/test_link_handshake.c1
-rw-r--r--src/test/test_util.c30
-rw-r--r--src/test/testing_common.c2
98 files changed, 3303 insertions, 1073 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c
index bbf984ad08..57aa055e73 100644
--- a/src/app/config/config.c
+++ b/src/app/config/config.c
@@ -510,6 +510,8 @@ static const config_var_t option_vars_[] = {
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceEnableIntroDoSBurstPerSec",
LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceOnionBalanceInstance",
+ LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
V(ClientOnionAuthDir, FILENAME, NULL),
diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c
index 27d92609eb..aac15246b9 100644
--- a/src/app/main/shutdown.c
+++ b/src/app/main/shutdown.c
@@ -75,7 +75,8 @@ tor_cleanup(void)
/* Remove Extended ORPort cookie authentication file */
{
char *cookie_fname = get_ext_or_auth_cookie_file_name();
- tor_remove_file(cookie_fname);
+ if (cookie_fname)
+ tor_remove_file(cookie_fname);
tor_free(cookie_fname);
}
if (accounting_is_enabled(options))
diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c
index b4439cdc7b..e32083537f 100644
--- a/src/app/main/subsystem_list.c
+++ b/src/app/main/subsystem_list.c
@@ -14,9 +14,7 @@
#include "lib/cc/torint.h"
#include "core/mainloop/mainloop_sys.h"
-#include "core/or/ocirc_event_sys.h"
#include "core/or/or_sys.h"
-#include "core/or/orconn_event_sys.h"
#include "feature/control/btrack_sys.h"
#include "lib/compress/compress_sys.h"
#include "lib/crypt_ops/crypto_sys.h"
@@ -24,7 +22,7 @@
#include "lib/log/log_sys.h"
#include "lib/net/network_sys.h"
#include "lib/process/process_sys.h"
-#include "lib/process/winprocess_sys.h"
+#include "lib/llharden/winprocess_sys.h"
#include "lib/thread/thread_sys.h"
#include "lib/time/time_sys.h"
#include "lib/tls/tortls_sys.h"
@@ -46,28 +44,26 @@ const subsys_fns_t *tor_subsystems[] = {
&sys_torerr,
&sys_wallclock,
- &sys_threads,
&sys_logging,
+ &sys_threads,
&sys_time,
- &sys_network,
- &sys_compress,
&sys_crypto,
+ &sys_compress,
+ &sys_network,
&sys_tortls,
- &sys_process,
-
- &sys_orconn_event,
- &sys_ocirc_event,
- &sys_btrack,
&sys_evloop,
+ &sys_process,
&sys_mainloop,
&sys_or,
&sys_relay,
+ &sys_btrack,
+
&sys_dirauth,
};
diff --git a/src/core/crypto/hs_ntor.c b/src/core/crypto/hs_ntor.c
index 2bd4c32446..07bcdc566c 100644
--- a/src/core/crypto/hs_ntor.c
+++ b/src/core/crypto/hs_ntor.c
@@ -170,7 +170,7 @@ get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
* necessary key material, and return 0. */
static void
get_introduce1_key_material(const uint8_t *secret_input,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
{
uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
@@ -181,7 +181,7 @@ get_introduce1_key_material(const uint8_t *secret_input,
/* Let's build info */
ptr = info_blob;
APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
- APPEND(ptr, subcredential, DIGEST256_LEN);
+ APPEND(ptr, subcredential->subcred, SUBCRED_LEN);
tor_assert(ptr == info_blob + sizeof(info_blob));
/* Let's build the input to the KDF */
@@ -317,7 +317,7 @@ hs_ntor_client_get_introduce1_keys(
const ed25519_public_key_t *intro_auth_pubkey,
const curve25519_public_key_t *intro_enc_pubkey,
const curve25519_keypair_t *client_ephemeral_enc_keypair,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
{
int bad = 0;
@@ -450,9 +450,31 @@ hs_ntor_service_get_introduce1_keys(
const ed25519_public_key_t *intro_auth_pubkey,
const curve25519_keypair_t *intro_enc_keypair,
const curve25519_public_key_t *client_ephemeral_enc_pubkey,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
{
+ return hs_ntor_service_get_introduce1_keys_multi(
+ intro_auth_pubkey,
+ intro_enc_keypair,
+ client_ephemeral_enc_pubkey,
+ 1,
+ subcredential,
+ hs_ntor_intro_cell_keys_out);
+}
+
+/**
+ * As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials
+ * as input, and yield multiple sets of keys as output.
+ **/
+int
+hs_ntor_service_get_introduce1_keys_multi(
+ const struct ed25519_public_key_t *intro_auth_pubkey,
+ const struct curve25519_keypair_t *intro_enc_keypair,
+ const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
+ size_t n_subcredentials,
+ const hs_subcredential_t *subcredentials,
+ hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
+{
int bad = 0;
uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
uint8_t dh_result[CURVE25519_OUTPUT_LEN];
@@ -460,7 +482,8 @@ hs_ntor_service_get_introduce1_keys(
tor_assert(intro_auth_pubkey);
tor_assert(intro_enc_keypair);
tor_assert(client_ephemeral_enc_pubkey);
- tor_assert(subcredential);
+ tor_assert(n_subcredentials >= 1);
+ tor_assert(subcredentials);
tor_assert(hs_ntor_intro_cell_keys_out);
/* Compute EXP(X, b) */
@@ -476,13 +499,16 @@ hs_ntor_service_get_introduce1_keys(
secret_input);
bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
- /* Get ENC_KEY and MAC_KEY! */
- get_introduce1_key_material(secret_input, subcredential,
- hs_ntor_intro_cell_keys_out);
+ for (unsigned i = 0; i < n_subcredentials; ++i) {
+ /* Get ENC_KEY and MAC_KEY! */
+ get_introduce1_key_material(secret_input, &subcredentials[i],
+ &hs_ntor_intro_cell_keys_out[i]);
+ }
memwipe(secret_input, 0, sizeof(secret_input));
if (bad) {
- memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
+ memwipe(hs_ntor_intro_cell_keys_out, 0,
+ sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
}
return bad ? -1 : 0;
diff --git a/src/core/crypto/hs_ntor.h b/src/core/crypto/hs_ntor.h
index 2bce5686cd..9a975dd83f 100644
--- a/src/core/crypto/hs_ntor.h
+++ b/src/core/crypto/hs_ntor.h
@@ -35,11 +35,20 @@ typedef struct hs_ntor_rend_cell_keys_t {
uint8_t ntor_key_seed[DIGEST256_LEN];
} hs_ntor_rend_cell_keys_t;
+#define SUBCRED_LEN DIGEST256_LEN
+
+/**
+ * A 'subcredential' used to prove knowledge of a hidden service.
+ **/
+typedef struct hs_subcredential_t {
+ uint8_t subcred[SUBCRED_LEN];
+} hs_subcredential_t;
+
int hs_ntor_client_get_introduce1_keys(
const struct ed25519_public_key_t *intro_auth_pubkey,
const struct curve25519_public_key_t *intro_enc_pubkey,
const struct curve25519_keypair_t *client_ephemeral_enc_keypair,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
int hs_ntor_client_get_rendezvous1_keys(
@@ -49,11 +58,19 @@ int hs_ntor_client_get_rendezvous1_keys(
const struct curve25519_public_key_t *service_ephemeral_rend_pubkey,
hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out);
+int hs_ntor_service_get_introduce1_keys_multi(
+ const struct ed25519_public_key_t *intro_auth_pubkey,
+ const struct curve25519_keypair_t *intro_enc_keypair,
+ const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
+ size_t n_subcredentials,
+ const hs_subcredential_t *subcredentials,
+ hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
+
int hs_ntor_service_get_introduce1_keys(
const struct ed25519_public_key_t *intro_auth_pubkey,
const struct curve25519_keypair_t *intro_enc_keypair,
const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out);
int hs_ntor_service_get_rendezvous1_keys(
diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c
index 48f31922d7..4965c2a1f4 100644
--- a/src/core/mainloop/connection.c
+++ b/src/core/mainloop/connection.c
@@ -91,6 +91,7 @@
#include "feature/control/control.h"
#include "feature/control/control_events.h"
#include "feature/dirauth/authmode.h"
+#include "feature/dirauth/dirauth_config.h"
#include "feature/dircache/dirserv.h"
#include "feature/dircommon/directory.h"
#include "feature/hibernate/hibernate.h"
@@ -3137,7 +3138,7 @@ connection_mark_all_noncontrol_connections(void)
* uses pluggable transports, since we should then limit it even if it
* comes from an internal IP address. */
static int
-connection_is_rate_limited(connection_t *conn)
+connection_is_rate_limited(const connection_t *conn)
{
const or_options_t *options = get_options();
if (conn->linked)
@@ -3272,14 +3273,14 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
global_bucket_val, conn_bucket);
}
-/** Return 1 if the global write buckets are low enough that we
+/** Return true iff the global write buckets are low enough that we
* shouldn't send <b>attempt</b> bytes of low-priority directory stuff
- * out to <b>conn</b>. Else return 0.
-
- * Priority was 1 for v1 requests (directories and running-routers),
- * and 2 for v2 requests and later (statuses and descriptors).
+ * out to <b>conn</b>.
+ *
+ * If we are a directory authority, always answer dir requests thus true is
+ * always returned.
*
- * There are a lot of parameters we could use here:
+ * Note: There are a lot of parameters we could use here:
* - global_relayed_write_bucket. Low is bad.
* - global_write_bucket. Low is bad.
* - bandwidthrate. Low is bad.
@@ -3291,39 +3292,40 @@ connection_bucket_write_limit(connection_t *conn, time_t now)
* mean is "total directory bytes added to outbufs recently", but
* that's harder to quantify and harder to keep track of.
*/
-int
-global_write_bucket_low(connection_t *conn, size_t attempt, int priority)
+bool
+connection_dir_is_global_write_low(const connection_t *conn, size_t attempt)
{
size_t smaller_bucket =
MIN(token_bucket_rw_get_write(&global_bucket),
token_bucket_rw_get_write(&global_relayed_bucket));
- if (authdir_mode(get_options()) && priority>1)
- return 0; /* there's always room to answer v2 if we're an auth dir */
+
+ /* Special case for authorities (directory only). */
+ if (authdir_mode_v3(get_options())) {
+ /* Are we configured to possibly reject requests under load? */
+ if (!dirauth_should_reject_requests_under_load()) {
+ /* Answer request no matter what. */
+ return false;
+ }
+ /* Always answer requests from a known relay which includes the other
+ * authorities. The following looks up the addresses for relays that we
+ * have their descriptor _and_ any configured trusted directories. */
+ if (nodelist_probably_contains_address(&conn->addr)) {
+ return false;
+ }
+ }
if (!connection_is_rate_limited(conn))
- return 0; /* local conns don't get limited */
+ return false; /* local conns don't get limited */
if (smaller_bucket < attempt)
- return 1; /* not enough space no matter the priority */
+ return true; /* not enough space. */
{
const time_t diff = approx_time() - write_buckets_last_empty_at;
if (diff <= 1)
- return 1; /* we're already hitting our limits, no more please */
+ return true; /* we're already hitting our limits, no more please */
}
-
- if (priority == 1) { /* old-style v1 query */
- /* Could we handle *two* of these requests within the next two seconds? */
- const or_options_t *options = get_options();
- size_t can_write = (size_t) (smaller_bucket
- + 2*(options->RelayBandwidthRate ? options->RelayBandwidthRate :
- options->BandwidthRate));
- if (can_write < 2*attempt)
- return 1;
- } else { /* v2 query */
- /* no further constraints yet */
- }
- return 0;
+ return false;
}
/** When did we last tell the accounting subsystem about transmitted
diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h
index 0ab601d86f..bcd3d590a5 100644
--- a/src/core/mainloop/connection.h
+++ b/src/core/mainloop/connection.h
@@ -219,8 +219,8 @@ void connection_mark_all_noncontrol_listeners(void);
void connection_mark_all_noncontrol_connections(void);
ssize_t connection_bucket_write_limit(struct connection_t *conn, time_t now);
-int global_write_bucket_low(struct connection_t *conn,
- size_t attempt, int priority);
+bool connection_dir_is_global_write_low(const struct connection_t *conn,
+ size_t attempt);
void connection_bucket_init(void);
void connection_bucket_adjust(const struct or_options_t *options);
void connection_bucket_refill_all(time_t now,
diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c
index 260de181e5..e4e17f6b76 100644
--- a/src/core/mainloop/mainloop.c
+++ b/src/core/mainloop/mainloop.c
@@ -966,7 +966,6 @@ conn_close_if_marked(int i)
return 0; /* nothing to see here, move along */
now = time(NULL);
assert_connection_ok(conn, now);
- /* assert_all_pending_dns_resolves_ok(); */
log_debug(LD_NET,"Cleaning up connection (fd "TOR_SOCKET_T_FORMAT").",
conn->s);
diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c
index 7974da4832..2a35237d30 100644
--- a/src/core/or/channeltls.c
+++ b/src/core/or/channeltls.c
@@ -47,6 +47,7 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
+#include "feature/relay/relay_handshake.h"
#include "feature/control/control.h"
#include "feature/client/entrynodes.h"
#include "trunnel/link_handshake.h"
diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c
index 03ed2c7d29..003b91af8d 100644
--- a/src/core/or/circuitbuild.c
+++ b/src/core/or/circuitbuild.c
@@ -2819,8 +2819,8 @@ extend_info_dup(extend_info_t *info)
* If there is no chosen exit, or if we don't know the node_t for
* the chosen exit, return NULL.
*/
-const node_t *
-build_state_get_exit_node(cpath_build_state_t *state)
+MOCK_IMPL(const node_t *,
+build_state_get_exit_node,(cpath_build_state_t *state))
{
if (!state || !state->chosen_exit)
return NULL;
diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h
index f5a3439064..48592dd346 100644
--- a/src/core/or/circuitbuild.h
+++ b/src/core/or/circuitbuild.h
@@ -66,7 +66,8 @@ int circuit_can_use_tap(const origin_circuit_t *circ);
int circuit_has_usable_onion_key(const origin_circuit_t *circ);
int extend_info_has_preferred_onion_key(const extend_info_t* ei);
const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state);
-const node_t *build_state_get_exit_node(cpath_build_state_t *state);
+MOCK_DECL(const node_t *,
+ build_state_get_exit_node,(cpath_build_state_t *state));
const char *build_state_get_exit_nickname(cpath_build_state_t *state);
struct circuit_guard_state_t;
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index aeb9ec6460..23c6e230cb 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -432,6 +432,21 @@ warn_if_hs_unreachable(const edge_connection_t *conn, uint8_t reason)
}
}
+/** Given a TTL (in seconds) from a DNS response or from a relay, determine
+ * what TTL clients and relays should actually use for caching it. */
+uint32_t
+clip_dns_ttl(uint32_t ttl)
+{
+ /* This logic is a defense against "DefectTor" DNS-based traffic
+ * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
+ * We only give two values: a "low" value and a "high" value.
+ */
+ if (ttl < MIN_DNS_TTL)
+ return MIN_DNS_TTL;
+ else
+ return MAX_DNS_TTL;
+}
+
/** Send a relay end cell from stream <b>conn</b> down conn's circuit, and
* remember that we've done so. If this is not a client connection, set the
* relay end cell's reason for closing as <b>reason</b>.
@@ -480,7 +495,7 @@ connection_edge_end(edge_connection_t *conn, uint8_t reason)
memcpy(payload+1, tor_addr_to_in6_addr8(&conn->base_.addr), 16);
addrlen = 16;
}
- set_uint32(payload+1+addrlen, htonl(dns_clip_ttl(conn->address_ttl)));
+ set_uint32(payload+1+addrlen, htonl(clip_dns_ttl(conn->address_ttl)));
payload_len += 4+addrlen;
}
@@ -845,7 +860,7 @@ connected_cell_format_payload(uint8_t *payload_out,
return -1;
}
- set_uint32(payload_out + connected_payload_len, htonl(dns_clip_ttl(ttl)));
+ set_uint32(payload_out + connected_payload_len, htonl(clip_dns_ttl(ttl)));
connected_payload_len += 4;
tor_assert(connected_payload_len <= MAX_CONNECTED_CELL_PAYLOAD_LEN);
diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h
index 11cb252935..8c06af5664 100644
--- a/src/core/or/connection_edge.h
+++ b/src/core/or/connection_edge.h
@@ -182,6 +182,21 @@ void connection_ap_warn_and_unmark_if_pending_circ(
entry_connection_t *entry_conn,
const char *where);
+/** Lowest value for DNS ttl that a server should give or a client should
+ * believe. */
+#define MIN_DNS_TTL (5*60)
+/** Highest value for DNS ttl that a server should give or a client should
+ * believe. */
+#define MAX_DNS_TTL (60*60)
+/** How long do we keep DNS cache entries before purging them (regardless of
+ * their TTL)? */
+#define MAX_DNS_ENTRY_AGE (3*60*60)
+/** How long do we cache/tell clients to cache DNS records when no TTL is
+ * known? */
+#define DEFAULT_DNS_TTL (30*60)
+
+uint32_t clip_dns_ttl(uint32_t ttl);
+
int connection_half_edge_is_valid_data(const smartlist_t *half_conns,
streamid_t stream_id);
int connection_half_edge_is_valid_sendme(const smartlist_t *half_conns,
diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c
index 76bfbf0b30..6dd646637b 100644
--- a/src/core/or/connection_or.c
+++ b/src/core/or/connection_or.c
@@ -39,14 +39,13 @@
#include "app/config/config.h"
#include "core/mainloop/connection.h"
#include "core/or/connection_or.h"
+#include "feature/relay/relay_handshake.h"
#include "feature/control/control_events.h"
-#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/dirauth/reachability.h"
#include "feature/client/entrynodes.h"
#include "lib/geoip/geoip.h"
#include "core/mainloop/mainloop.h"
-#include "trunnel/link_handshake.h"
#include "trunnel/netinfo.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
@@ -78,7 +77,6 @@
#include "lib/crypt_ops/crypto_format.h"
#include "lib/tls/tortls.h"
-#include "lib/tls/x509.h"
#include "core/or/orconn_event.h"
@@ -109,10 +107,6 @@ TO_OR_CONN(connection_t *c)
return DOWNCAST(or_connection_t, c);
}
-/** Global map between Extended ORPort identifiers and OR
- * connections. */
-static digestmap_t *orconn_ext_or_id_map = NULL;
-
/** Clear clear conn->identity_digest and update other data
* structures as appropriate.*/
void
@@ -198,71 +192,6 @@ connection_or_set_identity_digest(or_connection_t *conn,
channel_set_identity_digest(chan, rsa_digest, ed_id);
}
-/** Remove the Extended ORPort identifier of <b>conn</b> from the
- * global identifier list. Also, clear the identifier from the
- * connection itself. */
-void
-connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
-{
- or_connection_t *tmp;
- if (!orconn_ext_or_id_map)
- return;
- if (!conn->ext_or_conn_id)
- return;
-
- tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
- if (!tor_digest_is_zero(conn->ext_or_conn_id))
- tor_assert(tmp == conn);
-
- memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
-}
-
-/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
- * connection is found. */
-or_connection_t *
-connection_or_get_by_ext_or_id(const char *id)
-{
- if (!orconn_ext_or_id_map)
- return NULL;
- return digestmap_get(orconn_ext_or_id_map, id);
-}
-
-/** Deallocate the global Extended ORPort identifier list */
-void
-connection_or_clear_ext_or_id_map(void)
-{
- digestmap_free(orconn_ext_or_id_map, NULL);
- orconn_ext_or_id_map = NULL;
-}
-
-/** Creates an Extended ORPort identifier for <b>conn</b> and deposits
- * it into the global list of identifiers. */
-void
-connection_or_set_ext_or_identifier(or_connection_t *conn)
-{
- char random_id[EXT_OR_CONN_ID_LEN];
- or_connection_t *tmp;
-
- if (!orconn_ext_or_id_map)
- orconn_ext_or_id_map = digestmap_new();
-
- /* Remove any previous identifiers: */
- if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
- connection_or_remove_from_ext_or_id_map(conn);
-
- do {
- crypto_rand(random_id, sizeof(random_id));
- } while (digestmap_get(orconn_ext_or_id_map, random_id));
-
- if (!conn->ext_or_conn_id)
- conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
-
- memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
-
- tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
- tor_assert(!tmp);
-}
-
/**************************************************************/
/** Map from a string describing what a non-open OR connection was doing when
@@ -2596,533 +2525,3 @@ connection_or_send_netinfo,(or_connection_t *conn))
return r;
}
-
-/** Helper used to add an encoded certs to a cert cell */
-static void
-add_certs_cell_cert_helper(certs_cell_t *certs_cell,
- uint8_t cert_type,
- const uint8_t *cert_encoded,
- size_t cert_len)
-{
- tor_assert(cert_len <= UINT16_MAX);
- certs_cell_cert_t *ccc = certs_cell_cert_new();
- ccc->cert_type = cert_type;
- ccc->cert_len = cert_len;
- certs_cell_cert_setlen_body(ccc, cert_len);
- memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len);
-
- certs_cell_add_certs(certs_cell, ccc);
-}
-
-/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at
- * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are
- * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>.
- * (If <b>cert</b> is NULL, take no action.) */
-static void
-add_x509_cert(certs_cell_t *certs_cell,
- uint8_t cert_type,
- const tor_x509_cert_t *cert)
-{
- if (NULL == cert)
- return;
-
- const uint8_t *cert_encoded = NULL;
- size_t cert_len;
- tor_x509_cert_get_der(cert, &cert_encoded, &cert_len);
-
- add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len);
-}
-
-/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object
- * that we are building in <b>certs_cell</b>. Set its type field to
- * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */
-static void
-add_ed25519_cert(certs_cell_t *certs_cell,
- uint8_t cert_type,
- const tor_cert_t *cert)
-{
- if (NULL == cert)
- return;
-
- add_certs_cell_cert_helper(certs_cell, cert_type,
- cert->encoded, cert->encoded_len);
-}
-
-#ifdef TOR_UNIT_TESTS
-int certs_cell_ed25519_disabled_for_testing = 0;
-#else
-#define certs_cell_ed25519_disabled_for_testing 0
-#endif
-
-/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1
- * on failure. */
-int
-connection_or_send_certs_cell(or_connection_t *conn)
-{
- const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL;
- tor_x509_cert_t *own_link_cert = NULL;
- var_cell_t *cell;
-
- certs_cell_t *certs_cell = NULL;
-
- tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
-
- if (! conn->handshake_state)
- return -1;
-
- const int conn_in_server_mode = ! conn->handshake_state->started_here;
-
- /* Get the encoded values of the X509 certificates */
- if (tor_tls_get_my_certs(conn_in_server_mode,
- &global_link_cert, &id_cert) < 0)
- return -1;
-
- if (conn_in_server_mode) {
- own_link_cert = tor_tls_get_own_cert(conn->tls);
- }
- tor_assert(id_cert);
-
- certs_cell = certs_cell_new();
-
- /* Start adding certs. First the link cert or auth1024 cert. */
- if (conn_in_server_mode) {
- tor_assert_nonfatal(own_link_cert);
- add_x509_cert(certs_cell,
- OR_CERT_TYPE_TLS_LINK, own_link_cert);
- } else {
- tor_assert(global_link_cert);
- add_x509_cert(certs_cell,
- OR_CERT_TYPE_AUTH_1024, global_link_cert);
- }
-
- /* Next the RSA->RSA ID cert */
- add_x509_cert(certs_cell,
- OR_CERT_TYPE_ID_1024, id_cert);
-
- /* Next the Ed25519 certs */
- add_ed25519_cert(certs_cell,
- CERTTYPE_ED_ID_SIGN,
- get_master_signing_key_cert());
- if (conn_in_server_mode) {
- tor_assert_nonfatal(conn->handshake_state->own_link_cert ||
- certs_cell_ed25519_disabled_for_testing);
- add_ed25519_cert(certs_cell,
- CERTTYPE_ED_SIGN_LINK,
- conn->handshake_state->own_link_cert);
- } else {
- add_ed25519_cert(certs_cell,
- CERTTYPE_ED_SIGN_AUTH,
- get_current_auth_key_cert());
- }
-
- /* And finally the crosscert. */
- {
- const uint8_t *crosscert=NULL;
- size_t crosscert_len;
- get_master_rsa_crosscert(&crosscert, &crosscert_len);
- if (crosscert) {
- add_certs_cell_cert_helper(certs_cell,
- CERTTYPE_RSA1024_ID_EDID,
- crosscert, crosscert_len);
- }
- }
-
- /* We've added all the certs; make the cell. */
- certs_cell->n_certs = certs_cell_getlen_certs(certs_cell);
-
- ssize_t alloc_len = certs_cell_encoded_len(certs_cell);
- tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX);
- cell = var_cell_new(alloc_len);
- cell->command = CELL_CERTS;
- ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell);
- tor_assert(enc_len > 0 && enc_len <= alloc_len);
- cell->payload_len = enc_len;
-
- connection_or_write_var_cell_to_buf(cell, conn);
- var_cell_free(cell);
- certs_cell_free(certs_cell);
- tor_x509_cert_free(own_link_cert);
-
- return 0;
-}
-
-#ifdef TOR_UNIT_TESTS
-int testing__connection_or_pretend_TLSSECRET_is_supported = 0;
-#else
-#define testing__connection_or_pretend_TLSSECRET_is_supported 0
-#endif
-
-/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that
- * we can send and receive. */
-int
-authchallenge_type_is_supported(uint16_t challenge_type)
-{
- switch (challenge_type) {
- case AUTHTYPE_RSA_SHA256_TLSSECRET:
-#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
- return 1;
-#else
- return testing__connection_or_pretend_TLSSECRET_is_supported;
-#endif
- case AUTHTYPE_ED25519_SHA256_RFC5705:
- return 1;
- case AUTHTYPE_RSA_SHA256_RFC5705:
- default:
- return 0;
- }
-}
-
-/** Return true iff <b>challenge_type_a</b> is one that we would rather
- * use than <b>challenge_type_b</b>. */
-int
-authchallenge_type_is_better(uint16_t challenge_type_a,
- uint16_t challenge_type_b)
-{
- /* Any supported type is better than an unsupported one;
- * all unsupported types are equally bad. */
- if (!authchallenge_type_is_supported(challenge_type_a))
- return 0;
- if (!authchallenge_type_is_supported(challenge_type_b))
- return 1;
- /* It happens that types are superior in numerically ascending order.
- * If that ever changes, this must change too. */
- return (challenge_type_a > challenge_type_b);
-}
-
-/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0
- * on success, -1 on failure. */
-int
-connection_or_send_auth_challenge_cell(or_connection_t *conn)
-{
- var_cell_t *cell = NULL;
- int r = -1;
- tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
-
- if (! conn->handshake_state)
- return -1;
-
- auth_challenge_cell_t *ac = auth_challenge_cell_new();
-
- tor_assert(sizeof(ac->challenge) == 32);
- crypto_rand((char*)ac->challenge, sizeof(ac->challenge));
-
- if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET))
- auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
- /* Disabled, because everything that supports this method also supports
- * the much-superior ED25519_SHA256_RFC5705 */
- /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */
- if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705))
- auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705);
- auth_challenge_cell_set_n_methods(ac,
- auth_challenge_cell_getlen_methods(ac));
-
- cell = var_cell_new(auth_challenge_cell_encoded_len(ac));
- ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len,
- ac);
- if (len != cell->payload_len) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Encoded auth challenge cell length not as expected");
- goto done;
- /* LCOV_EXCL_STOP */
- }
- cell->command = CELL_AUTH_CHALLENGE;
-
- connection_or_write_var_cell_to_buf(cell, conn);
- r = 0;
-
- done:
- var_cell_free(cell);
- auth_challenge_cell_free(ac);
-
- return r;
-}
-
-/** Compute the main body of an AUTHENTICATE cell that a client can use
- * to authenticate itself on a v3 handshake for <b>conn</b>. Return it
- * in a var_cell_t.
- *
- * If <b>server</b> is true, only calculate the first
- * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
- * determined by the rest of the handshake, and which match the provided value
- * exactly.
- *
- * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
- * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
- * that should be signed), but don't actually sign it.
- *
- * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
- * entire authenticator, signed with <b>signing_key</b>.
- *
- * Return the length of the cell body on success, and -1 on failure.
- */
-var_cell_t *
-connection_or_compute_authenticate_cell_body(or_connection_t *conn,
- const int authtype,
- crypto_pk_t *signing_key,
- const ed25519_keypair_t *ed_signing_key,
- int server)
-{
- auth1_t *auth = NULL;
- auth_ctx_t *ctx = auth_ctx_new();
- var_cell_t *result = NULL;
- int old_tlssecrets_algorithm = 0;
- const char *authtype_str = NULL;
-
- int is_ed = 0;
-
- /* assert state is reasonable XXXX */
- switch (authtype) {
- case AUTHTYPE_RSA_SHA256_TLSSECRET:
- authtype_str = "AUTH0001";
- old_tlssecrets_algorithm = 1;
- break;
- case AUTHTYPE_RSA_SHA256_RFC5705:
- authtype_str = "AUTH0002";
- break;
- case AUTHTYPE_ED25519_SHA256_RFC5705:
- authtype_str = "AUTH0003";
- is_ed = 1;
- break;
- default:
- tor_assert(0);
- break;
- }
-
- auth = auth1_new();
- ctx->is_ed = is_ed;
-
- /* Type: 8 bytes. */
- memcpy(auth1_getarray_type(auth), authtype_str, 8);
-
- {
- const tor_x509_cert_t *id_cert=NULL;
- const common_digests_t *my_digests, *their_digests;
- const uint8_t *my_id, *their_id, *client_id, *server_id;
- if (tor_tls_get_my_certs(server, NULL, &id_cert))
- goto err;
- my_digests = tor_x509_cert_get_id_digests(id_cert);
- their_digests =
- tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert);
- tor_assert(my_digests);
- tor_assert(their_digests);
- my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
- their_id = (uint8_t*)their_digests->d[DIGEST_SHA256];
-
- client_id = server ? their_id : my_id;
- server_id = server ? my_id : their_id;
-
- /* Client ID digest: 32 octets. */
- memcpy(auth->cid, client_id, 32);
-
- /* Server ID digest: 32 octets. */
- memcpy(auth->sid, server_id, 32);
- }
-
- if (is_ed) {
- const ed25519_public_key_t *my_ed_id, *their_ed_id;
- if (!conn->handshake_state->certs->ed_id_sign) {
- log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer.");
- goto err;
- }
- my_ed_id = get_master_identity_key();
- their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key;
-
- const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey;
- const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey;
-
- memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN);
- memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN);
- }
-
- {
- crypto_digest_t *server_d, *client_d;
- if (server) {
- server_d = conn->handshake_state->digest_sent;
- client_d = conn->handshake_state->digest_received;
- } else {
- client_d = conn->handshake_state->digest_sent;
- server_d = conn->handshake_state->digest_received;
- }
-
- /* Server log digest : 32 octets */
- crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
-
- /* Client log digest : 32 octets */
- crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
- }
-
- {
- /* Digest of cert used on TLS link : 32 octets. */
- tor_x509_cert_t *cert = NULL;
- if (server) {
- cert = tor_tls_get_own_cert(conn->tls);
- } else {
- cert = tor_tls_get_peer_cert(conn->tls);
- }
- if (!cert) {
- log_warn(LD_OR, "Unable to find cert when making %s data.",
- authtype_str);
- goto err;
- }
-
- memcpy(auth->scert,
- tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
-
- tor_x509_cert_free(cert);
- }
-
- /* HMAC of clientrandom and serverrandom using master key : 32 octets */
- if (old_tlssecrets_algorithm) {
- if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) {
- log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS "
- "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) "
- "which we don't support.");
- }
- } else {
- char label[128];
- tor_snprintf(label, sizeof(label),
- "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str);
- int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets,
- auth->cid, sizeof(auth->cid),
- label);
- if (r < 0) {
- if (r != -2)
- log_warn(LD_BUG, "TLS key export failed for unknown reason.");
- // If r == -2, this was openssl bug 7712.
- goto err;
- }
- }
-
- /* 8 octets were reserved for the current time, but we're trying to get out
- * of the habit of sending time around willynilly. Fortunately, nothing
- * checks it. That's followed by 16 bytes of nonce. */
- crypto_rand((char*)auth->rand, 24);
-
- ssize_t maxlen = auth1_encoded_len(auth, ctx);
- if (ed_signing_key && is_ed) {
- maxlen += ED25519_SIG_LEN;
- } else if (signing_key && !is_ed) {
- maxlen += crypto_pk_keysize(signing_key);
- }
-
- const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */
- result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen);
- uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN;
- const size_t outlen = maxlen;
- ssize_t len;
-
- result->command = CELL_AUTHENTICATE;
- set_uint16(result->payload, htons(authtype));
-
- if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data.");
- goto err;
- /* LCOV_EXCL_STOP */
- }
-
- if (server) {
- auth1_t *tmp = NULL;
- ssize_t len2 = auth1_parse(&tmp, out, len, ctx);
- if (!tmp) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that "
- "we just encoded");
- goto err;
- /* LCOV_EXCL_STOP */
- }
- result->payload_len = (tmp->end_of_signed - result->payload);
-
- auth1_free(tmp);
- if (len2 != len) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data.");
- goto err;
- /* LCOV_EXCL_STOP */
- }
- goto done;
- }
-
- if (ed_signing_key && is_ed) {
- ed25519_signature_t sig;
- if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Unable to sign ed25519 authentication data");
- goto err;
- /* LCOV_EXCL_STOP */
- }
- auth1_setlen_sig(auth, ED25519_SIG_LEN);
- memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN);
-
- } else if (signing_key && !is_ed) {
- auth1_setlen_sig(auth, crypto_pk_keysize(signing_key));
-
- char d[32];
- crypto_digest256(d, (char*)out, len, DIGEST_SHA256);
- int siglen = crypto_pk_private_sign(signing_key,
- (char*)auth1_getarray_sig(auth),
- auth1_getlen_sig(auth),
- d, 32);
- if (siglen < 0) {
- log_warn(LD_OR, "Unable to sign AUTH1 data.");
- goto err;
- }
-
- auth1_setlen_sig(auth, siglen);
- }
-
- len = auth1_encode(out, outlen, auth, ctx);
- if (len < 0) {
- /* LCOV_EXCL_START */
- log_warn(LD_BUG, "Unable to encode signed AUTH1 data.");
- goto err;
- /* LCOV_EXCL_STOP */
- }
- tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len);
- result->payload_len = len + AUTH_CELL_HEADER_LEN;
- set_uint16(result->payload+2, htons(len));
-
- goto done;
-
- err:
- var_cell_free(result);
- result = NULL;
- done:
- auth1_free(auth);
- auth_ctx_free(ctx);
- return result;
-}
-
-/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
- * success, -1 on failure */
-MOCK_IMPL(int,
-connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype))
-{
- var_cell_t *cell;
- crypto_pk_t *pk = tor_tls_get_my_client_auth_key();
- /* XXXX make sure we're actually supposed to send this! */
-
- if (!pk) {
- log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key");
- return -1;
- }
- if (! authchallenge_type_is_supported(authtype)) {
- log_warn(LD_BUG, "Tried to send authenticate cell with unknown "
- "authentication type %d", authtype);
- return -1;
- }
-
- cell = connection_or_compute_authenticate_cell_body(conn,
- authtype,
- pk,
- get_current_auth_keypair(),
- 0 /* not server */);
- if (! cell) {
- log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!");
- return -1;
- }
- connection_or_write_var_cell_to_buf(cell, conn);
- var_cell_free(cell);
-
- return 0;
-}
diff --git a/src/core/or/connection_or.h b/src/core/or/connection_or.h
index 90b21ad77b..02bc87a864 100644
--- a/src/core/or/connection_or.h
+++ b/src/core/or/connection_or.h
@@ -97,19 +97,6 @@ MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell,
or_connection_t *conn));
int connection_or_send_versions(or_connection_t *conn, int v3_plus);
MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn));
-int connection_or_send_certs_cell(or_connection_t *conn);
-int connection_or_send_auth_challenge_cell(or_connection_t *conn);
-int authchallenge_type_is_supported(uint16_t challenge_type);
-int authchallenge_type_is_better(uint16_t challenge_type_a,
- uint16_t challenge_type_b);
-var_cell_t *connection_or_compute_authenticate_cell_body(
- or_connection_t *conn,
- const int authtype,
- crypto_pk_t *signing_key,
- const struct ed25519_keypair_t *ed_signing_key,
- int server);
-MOCK_DECL(int,connection_or_send_authenticate_cell,
- (or_connection_t *conn, int type));
int is_or_protocol_version_known(uint16_t version);
@@ -144,7 +131,6 @@ MOCK_DECL(STATIC void,connection_or_change_state,
#endif /* defined(CONNECTION_OR_PRIVATE) */
#ifdef TOR_UNIT_TESTS
-extern int certs_cell_ed25519_disabled_for_testing;
extern int testing__connection_or_pretend_TLSSECRET_is_supported;
#endif
diff --git a/src/core/or/include.am b/src/core/or/include.am
index 4dd251d2e4..3626e76bed 100644
--- a/src/core/or/include.am
+++ b/src/core/or/include.am
@@ -74,13 +74,11 @@ noinst_HEADERS += \
src/core/or/or_periodic.h \
src/core/or/or_sys.h \
src/core/or/orconn_event.h \
- src/core/or/orconn_event_sys.h \
src/core/or/or_circuit_st.h \
src/core/or/or_connection_st.h \
src/core/or/or_handshake_certs_st.h \
src/core/or/or_handshake_state_st.h \
src/core/or/ocirc_event.h \
- src/core/or/ocirc_event_sys.h \
src/core/or/origin_circuit_st.h \
src/core/or/policies.h \
src/core/or/port_cfg_st.h \
diff --git a/src/core/or/ocirc_event.c b/src/core/or/ocirc_event.c
index 66992a0b5f..fa16459175 100644
--- a/src/core/or/ocirc_event.c
+++ b/src/core/or/ocirc_event.c
@@ -22,7 +22,7 @@
#include "core/or/cpath_build_state_st.h"
#include "core/or/ocirc_event.h"
-#include "core/or/ocirc_event_sys.h"
+#include "core/or/or_sys.h"
#include "core/or/origin_circuit_st.h"
#include "lib/subsys/subsys.h"
@@ -84,7 +84,7 @@ static dispatch_typefns_t ocirc_cevent_fns = {
.fmt_fn = ocirc_cevent_fmt,
};
-static int
+int
ocirc_add_pubsub(struct pubsub_connector_t *connector)
{
if (DISPATCH_REGISTER_TYPE(connector, ocirc_state, &ocirc_state_fns))
@@ -119,10 +119,3 @@ ocirc_cevent_publish(ocirc_cevent_msg_t *msg)
{
PUBLISH(ocirc_cevent, msg);
}
-
-const subsys_fns_t sys_ocirc_event = {
- .name = "ocirc_event",
- .supported = true,
- .level = -32,
- .add_pubsub = ocirc_add_pubsub,
-};
diff --git a/src/core/or/ocirc_event_sys.h b/src/core/or/ocirc_event_sys.h
deleted file mode 100644
index abb89b04a0..0000000000
--- a/src/core/or/ocirc_event_sys.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* Copyright (c) 2007-2020, The Tor Project, Inc. */
-
-/**
- * \file ocirc_event_sys.h
- * \brief Declare subsystem object for the origin circuit event module.
- **/
-
-#ifndef TOR_OCIRC_EVENT_SYS_H
-#define TOR_OCIRC_EVENT_SYS_H
-
-extern const struct subsys_fns_t sys_ocirc_event;
-
-#endif /* !defined(TOR_OCIRC_EVENT_SYS_H) */
diff --git a/src/core/or/or_sys.c b/src/core/or/or_sys.c
index 394b7945e1..126f5448cf 100644
--- a/src/core/or/or_sys.c
+++ b/src/core/or/or_sys.c
@@ -34,10 +34,22 @@ subsys_or_shutdown(void)
policies_free_all();
}
+static int
+subsys_or_add_pubsub(struct pubsub_connector_t *connector)
+{
+ int rv = 0;
+ if (orconn_add_pubsub(connector) < 0)
+ rv = -1;
+ if (ocirc_add_pubsub(connector) < 0)
+ rv = -1;
+ return rv;
+}
+
const struct subsys_fns_t sys_or = {
.name = "or",
.supported = true,
.level = 20,
.initialize = subsys_or_initialize,
.shutdown = subsys_or_shutdown,
+ .add_pubsub = subsys_or_add_pubsub,
};
diff --git a/src/core/or/or_sys.h b/src/core/or/or_sys.h
index 3ae09f7b52..7ee56c8682 100644
--- a/src/core/or/or_sys.h
+++ b/src/core/or/or_sys.h
@@ -14,4 +14,8 @@
extern const struct subsys_fns_t sys_or;
+struct pubsub_connector_t;
+int ocirc_add_pubsub(struct pubsub_connector_t *connector);
+int orconn_add_pubsub(struct pubsub_connector_t *connector);
+
#endif /* !defined(TOR_CORE_OR_OR_SYS_H) */
diff --git a/src/core/or/orconn_event.c b/src/core/or/orconn_event.c
index d0a06aa040..c30e2dd22f 100644
--- a/src/core/or/orconn_event.c
+++ b/src/core/or/orconn_event.c
@@ -22,7 +22,7 @@
#define ORCONN_EVENT_PRIVATE
#include "core/or/orconn_event.h"
-#include "core/or/orconn_event_sys.h"
+#include "core/or/or_sys.h"
DECLARE_PUBLISH(orconn_state);
DECLARE_PUBLISH(orconn_status);
@@ -65,7 +65,7 @@ static dispatch_typefns_t orconn_status_fns = {
.fmt_fn = orconn_status_fmt,
};
-static int
+int
orconn_add_pubsub(struct pubsub_connector_t *connector)
{
if (DISPATCH_REGISTER_TYPE(connector, orconn_state, &orconn_state_fns))
@@ -90,10 +90,3 @@ orconn_status_publish(orconn_status_msg_t *msg)
{
PUBLISH(orconn_status, msg);
}
-
-const subsys_fns_t sys_orconn_event = {
- .name = "orconn_event",
- .supported = true,
- .level = -33,
- .add_pubsub = orconn_add_pubsub,
-};
diff --git a/src/core/or/orconn_event_sys.h b/src/core/or/orconn_event_sys.h
deleted file mode 100644
index 02f0b8116b..0000000000
--- a/src/core/or/orconn_event_sys.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* Copyright (c) 2007-2020, The Tor Project, Inc. */
-
-/**
- * \file orconn_event_sys.h
- * \brief Declare subsystem object for the OR connection event module.
- **/
-#ifndef TOR_ORCONN_EVENT_SYS_H
-#define TOR_ORCONN_EVENT_SYS_H
-
-extern const struct subsys_fns_t sys_orconn_event;
-
-#endif /* !defined(TOR_ORCONN_EVENT_SYS_H) */
diff --git a/src/core/or/scheduler.c b/src/core/or/scheduler.c
index cd9aa54642..6633ccfe1f 100644
--- a/src/core/or/scheduler.c
+++ b/src/core/or/scheduler.c
@@ -502,7 +502,12 @@ scheduler_free_all(void)
the_scheduler = NULL;
}
-/** Mark a channel as no longer ready to accept writes. */
+/** Mark a channel as no longer ready to accept writes.
+ *
+ * Possible state changes:
+ * - SCHED_CHAN_PENDING -> SCHED_CHAN_WAITING_TO_WRITE
+ * - SCHED_CHAN_WAITING_FOR_CELLS -> SCHED_CHAN_IDLE
+ */
MOCK_IMPL(void,
scheduler_channel_doesnt_want_writes,(channel_t *chan))
{
@@ -513,31 +518,32 @@ scheduler_channel_doesnt_want_writes,(channel_t *chan))
return;
}
- /* If it's already in pending, we can put it in waiting_to_write */
if (chan->scheduler_state == SCHED_CHAN_PENDING) {
/*
- * It's in channels_pending, so it shouldn't be in any of
- * the other lists. It can't write any more, so it goes to
- * channels_waiting_to_write.
+ * It has cells but no longer can write, so it becomes
+ * SCHED_CHAN_WAITING_TO_WRITE. It's in channels_pending, so we
+ * should remove it from the list.
*/
smartlist_pqueue_remove(channels_pending,
scheduler_compare_channels,
offsetof(channel_t, sched_heap_idx),
chan);
scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
- } else {
+ } else if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) {
/*
- * It's not in pending, so it can't become waiting_to_write; it's
- * either not in any of the lists (nothing to do) or it's already in
- * waiting_for_cells (remove it, can't write any more).
+ * It does not have cells and no longer can write, so it becomes
+ * SCHED_CHAN_IDLE.
*/
- if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) {
- scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
- }
+ scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
}
}
-/** Mark a channel as having waiting cells. */
+/** Mark a channel as having waiting cells.
+ *
+ * Possible state changes:
+ * - SCHED_CHAN_WAITING_FOR_CELLS -> SCHED_CHAN_PENDING
+ * - SCHED_CHAN_IDLE -> SCHED_CHAN_WAITING_TO_WRITE
+ */
MOCK_IMPL(void,
scheduler_channel_has_waiting_cells,(channel_t *chan))
{
@@ -548,12 +554,11 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
return;
}
- /* First, check if it's also writeable */
if (chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS) {
/*
- * It's in channels_waiting_for_cells, so it shouldn't be in any of
- * the other lists. It has waiting cells now, so it goes to
- * channels_pending.
+ * It is able to write and now has cells, so it becomes
+ * SCHED_CHAN_PENDING. It must be added to the channels_pending
+ * list.
*/
scheduler_set_channel_state(chan, SCHED_CHAN_PENDING);
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
@@ -565,16 +570,12 @@ scheduler_channel_has_waiting_cells,(channel_t *chan))
/* If we made a channel pending, we potentially have scheduling work to
* do. */
the_scheduler->schedule();
- } else {
+ } else if (chan->scheduler_state == SCHED_CHAN_IDLE) {
/*
- * It's not in waiting_for_cells, so it can't become pending; it's
- * either not in any of the lists (we add it to waiting_to_write)
- * or it's already in waiting_to_write or pending (we do nothing)
+ * It is not able to write but now has cells, so it becomes
+ * SCHED_CHAN_WAITING_TO_WRITE.
*/
- if (!(chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE ||
- chan->scheduler_state == SCHED_CHAN_PENDING)) {
- scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
- }
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_TO_WRITE);
}
}
@@ -662,8 +663,12 @@ scheduler_release_channel,(channel_t *chan))
scheduler_set_channel_state(chan, SCHED_CHAN_IDLE);
}
-/** Mark a channel as ready to accept writes */
-
+/** Mark a channel as ready to accept writes.
+ * Possible state changes:
+ *
+ * - SCHED_CHAN_WAITING_TO_WRITE -> SCHED_CHAN_PENDING
+ * - SCHED_CHAN_IDLE -> SCHED_CHAN_WAITING_FOR_CELLS
+ */
void
scheduler_channel_wants_writes(channel_t *chan)
{
@@ -674,10 +679,11 @@ scheduler_channel_wants_writes(channel_t *chan)
return;
}
- /* If it's already in waiting_to_write, we can put it in pending */
if (chan->scheduler_state == SCHED_CHAN_WAITING_TO_WRITE) {
/*
- * It can write now, so it goes to channels_pending.
+ * It has cells and can now write, so it becomes
+ * SCHED_CHAN_PENDING. It must be added to the channels_pending
+ * list.
*/
scheduler_set_channel_state(chan, SCHED_CHAN_PENDING);
if (!SCHED_BUG(chan->sched_heap_idx != -1, chan)) {
@@ -688,15 +694,12 @@ scheduler_channel_wants_writes(channel_t *chan)
}
/* We just made a channel pending, we have scheduling work to do. */
the_scheduler->schedule();
- } else {
+ } else if (chan->scheduler_state == SCHED_CHAN_IDLE) {
/*
- * It's not in SCHED_CHAN_WAITING_TO_WRITE, so it can't become pending;
- * it's either idle and goes to WAITING_FOR_CELLS, or it's a no-op.
+ * It does not have cells but can now write, so it becomes
+ * SCHED_CHAN_WAITING_FOR_CELLS.
*/
- if (!(chan->scheduler_state == SCHED_CHAN_WAITING_FOR_CELLS ||
- chan->scheduler_state == SCHED_CHAN_PENDING)) {
- scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
- }
+ scheduler_set_channel_state(chan, SCHED_CHAN_WAITING_FOR_CELLS);
}
}
diff --git a/src/core/or/scheduler_kist.c b/src/core/or/scheduler_kist.c
index e56942be09..b6f5846456 100644
--- a/src/core/or/scheduler_kist.c
+++ b/src/core/or/scheduler_kist.c
@@ -463,6 +463,13 @@ MOCK_IMPL(void, channel_write_to_kernel, (channel_t *chan))
log_debug(LD_SCHED, "Writing %lu bytes to kernel for chan %" PRIu64,
(unsigned long)channel_outbuf_length(chan),
chan->global_identifier);
+ /* Note that 'connection_handle_write()' may change the scheduler state of
+ * the channel during the scheduling loop with
+ * 'connection_or_flushed_some()' -> 'scheduler_channel_wants_writes()'.
+ * This side-effect will only occur if the channel is currently in the
+ * 'SCHED_CHAN_WAITING_TO_WRITE' or 'SCHED_CHAN_IDLE' states, which KIST
+ * rarely uses, so it should be fine unless KIST begins using these states
+ * in the future. */
connection_handle_write(TO_CONN(BASE_CHAN_TO_TLS(chan)->conn), 0);
}
diff --git a/src/core/proto/proto_ext_or.h b/src/core/proto/proto_ext_or.h
index daac3e3eb7..3408599fb7 100644
--- a/src/core/proto/proto_ext_or.h
+++ b/src/core/proto/proto_ext_or.h
@@ -24,4 +24,11 @@ struct ext_or_cmd_t {
int fetch_ext_or_command_from_buf(struct buf_t *buf,
struct ext_or_cmd_t **out);
+ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+
+#define ext_or_cmd_free(cmd) \
+ FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd))
+
+void ext_or_cmd_free_(ext_or_cmd_t *cmd);
+
#endif /* !defined(TOR_PROTO_EXT_OR_H) */
diff --git a/src/feature/client/addressmap.c b/src/feature/client/addressmap.c
index 1a6958d38c..cc97166f36 100644
--- a/src/feature/client/addressmap.c
+++ b/src/feature/client/addressmap.c
@@ -23,7 +23,6 @@
#include "app/config/config.h"
#include "core/or/connection_edge.h"
#include "feature/control/control_events.h"
-#include "feature/relay/dns.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/routerset.h"
@@ -689,7 +688,7 @@ client_dns_set_addressmap_impl(entry_connection_t *for_conn,
if (ttl<0)
ttl = DEFAULT_DNS_TTL;
else
- ttl = dns_clip_ttl(ttl);
+ ttl = clip_dns_ttl(ttl);
if (exitname) {
/* XXXX fails to ever get attempts to get an exit address of
diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c
index a8ea9781a4..55069bb60a 100644
--- a/src/feature/client/transports.c
+++ b/src/feature/client/transports.c
@@ -1420,8 +1420,10 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s",
ext_or_addrport_tmp);
}
- smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
- cookie_file_loc);
+ if (cookie_file_loc) {
+ smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
+ cookie_file_loc);
+ }
tor_free(ext_or_addrport_tmp);
tor_free(cookie_file_loc);
diff --git a/src/feature/control/btrack.c b/src/feature/control/btrack.c
index 874150ee13..3595af0fcc 100644
--- a/src/feature/control/btrack.c
+++ b/src/feature/control/btrack.c
@@ -57,7 +57,7 @@ btrack_add_pubsub(pubsub_connector_t *connector)
const subsys_fns_t sys_btrack = {
.name = "btrack",
.supported = true,
- .level = -30,
+ .level = 55,
.initialize = btrack_init,
.shutdown = btrack_fini,
.add_pubsub = btrack_add_pubsub,
diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c
index 3aeeab3b31..ca16dc8424 100644
--- a/src/feature/dirauth/dirauth_config.c
+++ b/src/feature/dirauth/dirauth_config.c
@@ -27,6 +27,7 @@
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/bwauth.h"
#include "feature/dirauth/dirauth_periodic.h"
+#include "feature/dirauth/dirauth_sys.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/guardfraction.h"
#include "feature/dirauth/dirauth_options_st.h"
@@ -45,6 +46,14 @@
#define YES_IF_CHANGED_INT(opt) \
if (!CFG_EQ_INT(old_options, new_options, opt)) return 1;
+/** Return true iff we are configured to reject request under load for non
+ * relay connections. */
+bool
+dirauth_should_reject_requests_under_load(void)
+{
+ return !!dirauth_get_options()->AuthDirRejectRequestsUnderLoad;
+}
+
/**
* Legacy validation/normalization function for the dirauth mode options in
* options. Uses old_options as the previous options.
diff --git a/src/feature/dirauth/dirauth_config.h b/src/feature/dirauth/dirauth_config.h
index 2ebafd917e..1ec599717c 100644
--- a/src/feature/dirauth/dirauth_config.h
+++ b/src/feature/dirauth/dirauth_config.h
@@ -35,6 +35,8 @@ int options_act_dirauth_mtbf(const struct or_options_t *old_options);
int options_act_dirauth_stats(const struct or_options_t *old_options,
bool *print_notice_out);
+bool dirauth_should_reject_requests_under_load(void);
+
extern const struct config_format_t dirauth_options_fmt;
#else /* !defined(HAVE_MODULE_DIRAUTH) */
@@ -78,6 +80,8 @@ options_validate_dirauth_mode(const struct or_options_t *old_options,
#define options_act_dirauth_stats(old_options, print_notice_out) \
(((void)(old_options)),((void)(print_notice_out)),0)
+#define dirauth_should_reject_requests_under_load() (false)
+
#endif /* defined(HAVE_MODULE_DIRAUTH) */
#endif /* !defined(TOR_FEATURE_DIRAUTH_DIRAUTH_CONFIG_H) */
diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc
index 5939010fe7..21f4996c39 100644
--- a/src/feature/dirauth/dirauth_options.inc
+++ b/src/feature/dirauth/dirauth_options.inc
@@ -95,4 +95,11 @@ CONF_VAR(TestingMinFastFlagThreshold, MEMUNIT, 0, "0")
* versions? */
CONF_VAR(VersioningAuthoritativeDirectory, BOOL, 0, "0")
+/** Boolean: Under bandwidth pressure, if set to 1, the authority will always
+ * answer directory requests from relays but will start sending 503 error code
+ * for the other connections. If set to 0, all connections are considered the
+ * same and the authority will try to answer them all regardless of bandwidth
+ * pressure or not. */
+CONF_VAR(AuthDirRejectRequestsUnderLoad, BOOL, 0, "1")
+
END_CONF_STRUCT(dirauth_options_t)
diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c
index 4e0e19dc91..90e82a4ee8 100644
--- a/src/feature/dirauth/dirvote.c
+++ b/src/feature/dirauth/dirvote.c
@@ -2963,7 +2963,7 @@ dirvote_perform_vote(void)
if (!contents)
return -1;
- pending_vote = dirvote_add_vote(contents, &msg, &status);
+ pending_vote = dirvote_add_vote(contents, 0, &msg, &status);
tor_free(contents);
if (!pending_vote) {
log_warn(LD_DIR, "Couldn't store my own vote! (I told myself, '%s'.)",
@@ -3119,13 +3119,45 @@ list_v3_auth_ids(void)
return keys;
}
+/* Check the voter information <b>vi</b>, and assert that at least one
+ * signature is good. Asserts on failure. */
+static void
+assert_any_sig_good(const networkstatus_voter_info_t *vi)
+{
+ int any_sig_good = 0;
+ SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig,
+ if (sig->good_signature)
+ any_sig_good = 1);
+ tor_assert(any_sig_good);
+}
+
+/* Add <b>cert</b> to our list of known authority certificates. */
+static void
+add_new_cert_if_needed(const struct authority_cert_t *cert)
+{
+ tor_assert(cert);
+ if (!authority_cert_get_by_digests(cert->cache_info.identity_digest,
+ cert->signing_key_digest)) {
+ /* Hey, it's a new cert! */
+ trusted_dirs_load_certs_from_string(
+ cert->cache_info.signed_descriptor_body,
+ TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/,
+ NULL);
+ if (!authority_cert_get_by_digests(cert->cache_info.identity_digest,
+ cert->signing_key_digest)) {
+ log_warn(LD_BUG, "We added a cert, but still couldn't find it.");
+ }
+ }
+}
+
/** Called when we have received a networkstatus vote in <b>vote_body</b>.
* Parse and validate it, and on success store it as a pending vote (which we
* then return). Return NULL on failure. Sets *<b>msg_out</b> and
* *<b>status_out</b> to an HTTP response and status code. (V3 authority
* only) */
pending_vote_t *
-dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
+dirvote_add_vote(const char *vote_body, time_t time_posted,
+ const char **msg_out, int *status_out)
{
networkstatus_t *vote;
networkstatus_voter_info_t *vi;
@@ -3156,13 +3188,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
}
tor_assert(smartlist_len(vote->voters) == 1);
vi = get_voter(vote);
- {
- int any_sig_good = 0;
- SMARTLIST_FOREACH(vi->sigs, document_signature_t *, sig,
- if (sig->good_signature)
- any_sig_good = 1);
- tor_assert(any_sig_good);
- }
+ assert_any_sig_good(vi);
ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest);
if (!ds) {
char *keys = list_v3_auth_ids();
@@ -3175,19 +3201,7 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
*msg_out = "Vote not from a recognized v3 authority";
goto err;
}
- tor_assert(vote->cert);
- if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
- vote->cert->signing_key_digest)) {
- /* Hey, it's a new cert! */
- trusted_dirs_load_certs_from_string(
- vote->cert->cache_info.signed_descriptor_body,
- TRUSTED_DIRS_CERTS_SRC_FROM_VOTE, 1 /*flush*/,
- NULL);
- if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
- vote->cert->signing_key_digest)) {
- log_warn(LD_BUG, "We added a cert, but still couldn't find it.");
- }
- }
+ add_new_cert_if_needed(vote->cert);
/* Is it for the right period? */
if (vote->valid_after != voting_schedule.interval_starts) {
@@ -3200,6 +3214,23 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
goto err;
}
+ /* Check if we received it, as a post, after the cutoff when we
+ * start asking other dir auths for it. If we do, the best plan
+ * is to discard it, because using it greatly increases the chances
+ * of a split vote for this round (some dir auths got it in time,
+ * some didn't). */
+ if (time_posted && time_posted > voting_schedule.fetch_missing_votes) {
+ char tbuf1[ISO_TIME_LEN+1], tbuf2[ISO_TIME_LEN+1];
+ format_iso_time(tbuf1, time_posted);
+ format_iso_time(tbuf2, voting_schedule.fetch_missing_votes);
+ log_warn(LD_DIR, "Rejecting posted vote from %s received at %s; "
+ "our cutoff for received votes is %s. Check your clock, "
+ "CPU load, and network load. Also check the authority that "
+ "posted the vote.", vi->address, tbuf1, tbuf2);
+ *msg_out = "Posted vote received too late, would be dangerous to count it";
+ goto err;
+ }
+
/* Fetch any new router descriptors we just learned about */
update_consensus_router_descriptor_downloads(time(NULL), 1, vote);
diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h
index 305094aa49..f2dbee20e5 100644
--- a/src/feature/dirauth/dirvote.h
+++ b/src/feature/dirauth/dirvote.h
@@ -94,6 +94,7 @@ void dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items,
/* Storing signatures and votes functions */
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
+ time_t time_posted,
const char **msg_out,
int *status_out);
int dirvote_add_signatures(const char *detached_signatures_body,
@@ -142,9 +143,13 @@ dirvote_dirreq_get_status_vote(const char *url, smartlist_t *items,
}
static inline struct pending_vote_t *
-dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
+dirvote_add_vote(const char *vote_body,
+ time_t time_posted,
+ const char **msg_out,
+ int *status_out)
{
(void) vote_body;
+ (void) time_posted;
/* If the dirauth module is disabled, this should NEVER be called else we
* failed to safeguard the dirauth module. */
tor_assert_nonfatal_unreached();
diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c
index ef7054001e..4f7f209207 100644
--- a/src/feature/dircache/dircache.c
+++ b/src/feature/dircache/dircache.c
@@ -957,7 +957,7 @@ handle_get_current_consensus(dir_connection_t *conn,
goto done;
}
- if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_debug(LD_DIRSERV,
"Client asked for network status lists, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1066,7 +1066,7 @@ handle_get_status_vote(dir_connection_t *conn, const get_handler_args_t *args)
}
});
- if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), estimated_len)) {
write_short_http_response(conn, 503, "Directory busy, try again later");
goto vote_done;
}
@@ -1125,7 +1125,7 @@ handle_get_microdesc(dir_connection_t *conn, const get_handler_args_t *args)
write_short_http_response(conn, 404, "Not found");
goto done;
}
- if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1223,7 +1223,7 @@ handle_get_descriptor(dir_connection_t *conn, const get_handler_args_t *args)
msg = "Not found";
write_short_http_response(conn, 404, msg);
} else {
- if (global_write_bucket_low(TO_CONN(conn), size_guess, 2)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn), size_guess)) {
log_info(LD_DIRSERV,
"Client asked for server descriptors, but we've been "
"writing too many bytes lately. Sending 503 Dir busy.");
@@ -1319,9 +1319,8 @@ handle_get_keys(dir_connection_t *conn, const get_handler_args_t *args)
SMARTLIST_FOREACH(certs, authority_cert_t *, c,
len += c->cache_info.signed_descriptor_len);
- if (global_write_bucket_low(TO_CONN(conn),
- compress_method != NO_METHOD ? len/2 : len,
- 2)) {
+ if (connection_dir_is_global_write_low(TO_CONN(conn),
+ compress_method != NO_METHOD ? len/2 : len)) {
write_short_http_response(conn, 503, "Directory busy, try again later");
goto keys_done;
}
@@ -1696,7 +1695,7 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers,
!strcmp(url,"/tor/post/vote")) { /* v3 networkstatus vote */
const char *msg = "OK";
int status;
- if (dirvote_add_vote(body, &msg, &status)) {
+ if (dirvote_add_vote(body, approx_time(), &msg, &status)) {
write_short_http_response(conn, status, "Vote stored");
} else {
tor_assert(msg);
diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c
index 9b9cdd1926..a2ad69649c 100644
--- a/src/feature/dirclient/dirclient.c
+++ b/src/feature/dirclient/dirclient.c
@@ -2413,7 +2413,7 @@ handle_response_fetch_status_vote(dir_connection_t *conn,
conn->base_.port, conn->requested_resource);
return -1;
}
- dirvote_add_vote(body, &msg, &st);
+ dirvote_add_vote(body, 0, &msg, &st);
if (st > 299) {
log_warn(LD_DIR, "Error adding retrieved vote: %s", msg);
} else {
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 52bd663200..dd5fefd7e7 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -13,6 +13,7 @@
#include "feature/hs_common/replaycache.h"
#include "feature/hs/hs_cell.h"
+#include "feature/hs/hs_ob.h"
#include "core/crypto/hs_ntor.h"
#include "core/or/origin_circuit_st.h"
@@ -67,14 +68,17 @@ compute_introduce_mac(const uint8_t *encoded_cell, size_t encoded_cell_len,
memwipe(mac_msg, 0, sizeof(mac_msg));
}
-/** From a set of keys, subcredential and the ENCRYPTED section of an
- * INTRODUCE2 cell, return a newly allocated intro cell keys structure.
- * Finally, the client public key is copied in client_pk. On error, return
- * NULL. */
+/**
+ * From a set of keys, a list of subcredentials, and the ENCRYPTED section of
+ * an INTRODUCE2 cell, return an array of newly allocated intro cell keys
+ * structures. Finally, the client public key is copied in client_pk. On
+ * error, return NULL.
+ **/
static hs_ntor_intro_cell_keys_t *
get_introduce2_key_material(const ed25519_public_key_t *auth_key,
const curve25519_keypair_t *enc_key,
- const uint8_t *subcredential,
+ size_t n_subcredentials,
+ const hs_subcredential_t *subcredentials,
const uint8_t *encrypted_section,
curve25519_public_key_t *client_pk)
{
@@ -82,17 +86,19 @@ get_introduce2_key_material(const ed25519_public_key_t *auth_key,
tor_assert(auth_key);
tor_assert(enc_key);
- tor_assert(subcredential);
+ tor_assert(n_subcredentials > 0);
+ tor_assert(subcredentials);
tor_assert(encrypted_section);
tor_assert(client_pk);
- keys = tor_malloc_zero(sizeof(*keys));
+ keys = tor_calloc(n_subcredentials, sizeof(hs_ntor_intro_cell_keys_t));
/* First bytes of the ENCRYPTED section are the client public key. */
memcpy(client_pk->public_key, encrypted_section, CURVE25519_PUBKEY_LEN);
- if (hs_ntor_service_get_introduce1_keys(auth_key, enc_key, client_pk,
- subcredential, keys) < 0) {
+ if (hs_ntor_service_get_introduce1_keys_multi(auth_key, enc_key, client_pk,
+ n_subcredentials,
+ subcredentials, keys) < 0) {
/* Don't rely on the caller to wipe this on error. */
memwipe(client_pk, 0, sizeof(curve25519_public_key_t));
tor_free(keys);
@@ -747,6 +753,74 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
return ret;
}
+/** For the encrypted INTRO2 cell in <b>encrypted_section</b>, use the crypto
+ * material in <b>data</b> to compute the right ntor keys. Also validate the
+ * INTRO2 MAC to ensure that the keys are the right ones.
+ *
+ * Return NULL on failure to either produce the key material or on MAC
+ * valication. Else a newly allocated intro keys object. */
+static hs_ntor_intro_cell_keys_t *
+get_introduce2_keys_and_verify_mac(hs_cell_introduce2_data_t *data,
+ const uint8_t *encrypted_section,
+ size_t encrypted_section_len)
+{
+ hs_ntor_intro_cell_keys_t *intro_keys = NULL;
+ hs_ntor_intro_cell_keys_t *intro_keys_result = NULL;
+
+ /* Build the key material out of the key material found in the cell. */
+ intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
+ data->n_subcredentials,
+ data->subcredentials,
+ encrypted_section,
+ &data->client_pk);
+ if (intro_keys == NULL) {
+ log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
+ "compute key material");
+ return NULL;
+ }
+
+ /* Make sure we are not about to underflow. */
+ if (BUG(encrypted_section_len < DIGEST256_LEN)) {
+ return NULL;
+ }
+
+ /* Validate MAC from the cell and our computed key material. The MAC field
+ * in the cell is at the end of the encrypted section. */
+ intro_keys_result = tor_malloc_zero(sizeof(*intro_keys_result));
+ for (unsigned i = 0; i < data->n_subcredentials; ++i) {
+ uint8_t mac[DIGEST256_LEN];
+
+ /* The MAC field is at the very end of the ENCRYPTED section. */
+ size_t mac_offset = encrypted_section_len - sizeof(mac);
+ /* Compute the MAC. Use the entire encoded payload with a length up to the
+ * ENCRYPTED section. */
+ compute_introduce_mac(data->payload,
+ data->payload_len - encrypted_section_len,
+ encrypted_section, encrypted_section_len,
+ intro_keys[i].mac_key,
+ sizeof(intro_keys[i].mac_key),
+ mac, sizeof(mac));
+ /* Time-invariant conditional copy: if the MAC is what we expected, then
+ * set intro_keys_result to intro_keys[i]. Otherwise, don't: but don't
+ * leak which one it was! */
+ bool equal = tor_memeq(mac, encrypted_section + mac_offset, sizeof(mac));
+ memcpy_if_true_timei(equal, intro_keys_result, &intro_keys[i],
+ sizeof(*intro_keys_result));
+ }
+
+ /* We no longer need intro_keys. */
+ memwipe(intro_keys, 0,
+ sizeof(hs_ntor_intro_cell_keys_t) * data->n_subcredentials);
+ tor_free(intro_keys);
+
+ if (safe_mem_is_zero(intro_keys_result, sizeof(*intro_keys_result))) {
+ log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell");
+ tor_free(intro_keys_result); /* sets intro_keys_result to NULL */
+ }
+
+ return intro_keys_result;
+}
+
/** Parse the INTRODUCE2 cell using data which contains everything we need to
* do so and contains the destination buffers of information we extract and
* compute from the cell. Return 0 on success else a negative value. The
@@ -795,47 +869,29 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
/* Check our replay cache for this introduction point. */
if (replaycache_add_test_and_elapsed(data->replay_cache, encrypted_section,
encrypted_section_len, &elapsed)) {
- log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the"
+ log_warn(LD_REND, "Possible replay detected! An INTRODUCE2 cell with the "
"same ENCRYPTED section was seen %ld seconds ago. "
"Dropping cell.", (long int) elapsed);
goto done;
}
- /* Build the key material out of the key material found in the cell. */
- intro_keys = get_introduce2_key_material(data->auth_pk, data->enc_kp,
- data->subcredential,
- encrypted_section,
- &data->client_pk);
- if (intro_keys == NULL) {
- log_info(LD_REND, "Invalid INTRODUCE2 encrypted data. Unable to "
- "compute key material on circuit %u for service %s",
- TO_CIRCUIT(circ)->n_circ_id,
+ /* First bytes of the ENCRYPTED section are the client public key (they are
+ * guaranteed to exist because of the length check above). We are gonna use
+ * the client public key to compute the ntor keys and decrypt the payload:
+ */
+ memcpy(&data->client_pk.public_key, encrypted_section,
+ CURVE25519_PUBKEY_LEN);
+
+ /* Get the right INTRODUCE2 ntor keys and verify the cell MAC */
+ intro_keys = get_introduce2_keys_and_verify_mac(data, encrypted_section,
+ encrypted_section_len);
+ if (!intro_keys) {
+ log_warn(LD_REND, "Could not get valid INTRO2 keys on circuit %u "
+ "for service %s", TO_CIRCUIT(circ)->n_circ_id,
safe_str_client(service->onion_address));
goto done;
}
- /* Validate MAC from the cell and our computed key material. The MAC field
- * in the cell is at the end of the encrypted section. */
- {
- uint8_t mac[DIGEST256_LEN];
- /* The MAC field is at the very end of the ENCRYPTED section. */
- size_t mac_offset = encrypted_section_len - sizeof(mac);
- /* Compute the MAC. Use the entire encoded payload with a length up to the
- * ENCRYPTED section. */
- compute_introduce_mac(data->payload,
- data->payload_len - encrypted_section_len,
- encrypted_section, encrypted_section_len,
- intro_keys->mac_key, sizeof(intro_keys->mac_key),
- mac, sizeof(mac));
- if (tor_memcmp(mac, encrypted_section + mac_offset, sizeof(mac))) {
- log_info(LD_REND, "Invalid MAC validation for INTRODUCE2 cell on "
- "circuit %u for service %s",
- TO_CIRCUIT(circ)->n_circ_id,
- safe_str_client(service->onion_address));
- goto done;
- }
- }
-
{
/* The ENCRYPTED_DATA section starts just after the CLIENT_PK. */
const uint8_t *encrypted_data =
diff --git a/src/feature/hs/hs_cell.h b/src/feature/hs/hs_cell.h
index 80f37057d2..2b28c44c50 100644
--- a/src/feature/hs/hs_cell.h
+++ b/src/feature/hs/hs_cell.h
@@ -16,6 +16,8 @@
* 3.2.2 of the specification). Below this value, the cell must be padded. */
#define HS_CELL_INTRODUCE1_MIN_SIZE 246
+struct hs_subcredential_t;
+
/** This data structure contains data that we need to build an INTRODUCE1 cell
* used by the INTRODUCE1 build function. */
typedef struct hs_cell_introduce1_data_t {
@@ -29,7 +31,7 @@ typedef struct hs_cell_introduce1_data_t {
/** Introduction point encryption public key. */
const curve25519_public_key_t *enc_pk;
/** Subcredentials of the service. */
- const uint8_t *subcredential;
+ const struct hs_subcredential_t *subcredential;
/** Onion public key for the ntor handshake. */
const curve25519_public_key_t *onion_pk;
/** Rendezvous cookie. */
@@ -55,9 +57,14 @@ typedef struct hs_cell_introduce2_data_t {
owned by the introduction point object through which we received the
INTRO2 cell*/
const curve25519_keypair_t *enc_kp;
- /** Subcredentials of the service. Pointer owned by the descriptor that owns
- the introduction point through which we received the INTRO2 cell. */
- const uint8_t *subcredential;
+ /**
+ * Length of the subcredentials array below.
+ **/
+ size_t n_subcredentials;
+ /** Array of <b>n_subcredentials</b> subcredentials for the service. Pointer
+ * owned by the descriptor that owns the introduction point through which we
+ * received the INTRO2 cell. */
+ const struct hs_subcredential_t *subcredentials;
/** Payload of the received encoded cell. */
const uint8_t *payload;
/** Size of the payload of the received encoded cell. */
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index 90805a98b7..fdd226ba79 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -19,6 +19,7 @@
#include "feature/client/circpathbias.h"
#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_circuit.h"
+#include "feature/hs/hs_ob.h"
#include "feature/hs/hs_circuitmap.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_ident.h"
@@ -367,10 +368,10 @@ get_service_anonymity_string(const hs_service_t *service)
* success, a circuit identifier is attached to the circuit with the needed
* data. This function will try to open a circuit for a maximum value of
* MAX_REND_FAILURES then it will give up. */
-static void
-launch_rendezvous_point_circuit(const hs_service_t *service,
- const hs_service_intro_point_t *ip,
- const hs_cell_introduce2_data_t *data)
+MOCK_IMPL(STATIC void,
+launch_rendezvous_point_circuit,(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ const hs_cell_introduce2_data_t *data))
{
int circ_needs_uptime;
time_t now = time(NULL);
@@ -578,7 +579,7 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
static int
setup_introduce1_data(const hs_desc_intro_point_t *ip,
const node_t *rp_node,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
hs_cell_introduce1_data_t *intro1_data)
{
int ret = -1;
@@ -958,6 +959,42 @@ hs_circ_handle_intro_established(const hs_service_t *service,
return ret;
}
+/**
+ * Go into <b>data</b> and add the right subcredential to be able to handle
+ * this incoming cell.
+ *
+ * <b>desc_subcred</b> is the subcredential of the descriptor that corresponds
+ * to the intro point that received this intro request. This subcredential
+ * should be used if we are not an onionbalance instance.
+ *
+ * Return 0 if everything went well, or -1 in case of internal error.
+ */
+static int
+get_subcredential_for_handling_intro2_cell(const hs_service_t *service,
+ hs_cell_introduce2_data_t *data,
+ const hs_subcredential_t *desc_subcred)
+{
+ /* Handle the simple case first: We are not an onionbalance instance and we
+ * should just use the regular descriptor subcredential */
+ if (!hs_ob_service_is_instance(service)) {
+ data->n_subcredentials = 1;
+ data->subcredentials = desc_subcred;
+ return 0;
+ }
+
+ /* This should not happen since we should have made onionbalance
+ * subcredentials when we created our descriptors. */
+ if (BUG(!service->ob_subcreds)) {
+ return -1;
+ }
+
+ /* We are an onionbalance instance: */
+ data->n_subcredentials = service->n_ob_subcreds;
+ data->subcredentials = service->ob_subcreds;
+
+ return 0;
+}
+
/** We just received an INTRODUCE2 cell on the established introduction circuit
* circ. Handle the INTRODUCE2 payload of size payload_len for the given
* circuit and service. This cell is associated with the intro point object ip
@@ -966,7 +1003,7 @@ int
hs_circ_handle_introduce2(const hs_service_t *service,
const origin_circuit_t *circ,
hs_service_intro_point_t *ip,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
const uint8_t *payload, size_t payload_len)
{
int ret = -1;
@@ -983,12 +1020,16 @@ hs_circ_handle_introduce2(const hs_service_t *service,
* parsed, decrypted and key material computed correctly. */
data.auth_pk = &ip->auth_key_kp.pubkey;
data.enc_kp = &ip->enc_key_kp;
- data.subcredential = subcredential;
data.payload = payload;
data.payload_len = payload_len;
data.link_specifiers = smartlist_new();
data.replay_cache = ip->replay_cache;
+ if (get_subcredential_for_handling_intro2_cell(service,
+ &data, subcredential)) {
+ goto done;
+ }
+
if (hs_cell_parse_introduce2(&data, circ, service) < 0) {
goto done;
}
@@ -1092,7 +1133,7 @@ int
hs_circ_send_introduce1(origin_circuit_t *intro_circ,
origin_circuit_t *rend_circ,
const hs_desc_intro_point_t *ip,
- const uint8_t *subcredential)
+ const hs_subcredential_t *subcredential)
{
int ret = -1;
ssize_t payload_len;
diff --git a/src/feature/hs/hs_circuit.h b/src/feature/hs/hs_circuit.h
index 92231369c6..22e936e685 100644
--- a/src/feature/hs/hs_circuit.h
+++ b/src/feature/hs/hs_circuit.h
@@ -46,15 +46,16 @@ int hs_circ_handle_intro_established(const hs_service_t *service,
origin_circuit_t *circ,
const uint8_t *payload,
size_t payload_len);
+struct hs_subcredential_t;
int hs_circ_handle_introduce2(const hs_service_t *service,
const origin_circuit_t *circ,
hs_service_intro_point_t *ip,
- const uint8_t *subcredential,
+ const struct hs_subcredential_t *subcredential,
const uint8_t *payload, size_t payload_len);
int hs_circ_send_introduce1(origin_circuit_t *intro_circ,
origin_circuit_t *rend_circ,
const hs_desc_intro_point_t *ip,
- const uint8_t *subcredential);
+ const struct hs_subcredential_t *subcredential);
int hs_circ_send_establish_rendezvous(origin_circuit_t *circ);
/* e2e circuit API. */
@@ -78,6 +79,12 @@ create_rp_circuit_identifier(const hs_service_t *service,
const curve25519_public_key_t *server_pk,
const struct hs_ntor_rend_cell_keys_t *keys);
+struct hs_cell_introduce2_data_t;
+MOCK_DECL(STATIC void,
+launch_rendezvous_point_circuit,(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ const struct hs_cell_introduce2_data_t *data));
+
#endif /* defined(HS_CIRCUIT_PRIVATE) */
#endif /* !defined(TOR_HS_CIRCUIT_H) */
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 222261e604..d5c1c5ca9a 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -646,7 +646,7 @@ send_introduce1(origin_circuit_t *intro_circ,
/* Send the INTRODUCE1 cell. */
if (hs_circ_send_introduce1(intro_circ, rend_circ, ip,
- desc->subcredential) < 0) {
+ &desc->subcredential) < 0) {
if (TO_CIRCUIT(intro_circ)->marked_for_close) {
/* If the introduction circuit was closed, we were unable to send the
* cell for some reasons. In any case, the intro circuit has to be
@@ -1845,7 +1845,7 @@ hs_client_decode_descriptor(const char *desc_str,
hs_descriptor_t **desc)
{
hs_desc_decode_status_t ret;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
ed25519_public_key_t blinded_pubkey;
hs_client_service_authorization_t *client_auth = NULL;
curve25519_secret_key_t *client_auht_sk = NULL;
@@ -1865,13 +1865,13 @@ hs_client_decode_descriptor(const char *desc_str,
uint64_t current_time_period = hs_get_time_period_num(0);
hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period,
&blinded_pubkey);
- hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential);
+ hs_get_subcredential(service_identity_pk, &blinded_pubkey, &subcredential);
}
/* Parse descriptor */
- ret = hs_desc_decode_descriptor(desc_str, subcredential,
+ ret = hs_desc_decode_descriptor(desc_str, &subcredential,
client_auht_sk, desc);
- memwipe(subcredential, 0, sizeof(subcredential));
+ memwipe(&subcredential, 0, sizeof(subcredential));
if (ret != HS_DESC_DECODE_OK) {
goto err;
}
@@ -2486,4 +2486,3 @@ set_hs_client_auths_map(digest256map_t *map)
}
#endif /* defined(TOR_UNIT_TESTS) */
-
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index f8b031cc26..4639cdb68a 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -22,6 +22,7 @@
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_dos.h"
+#include "feature/hs/hs_ob.h"
#include "feature/hs/hs_ident.h"
#include "feature/hs/hs_service.h"
#include "feature/hs_common/shared_random_client.h"
@@ -808,12 +809,12 @@ hs_parse_address_impl(const char *address, ed25519_public_key_t *key_out,
}
/** Using the given identity public key and a blinded public key, compute the
- * subcredential and put it in subcred_out (must be of size DIGEST256_LEN).
+ * subcredential and put it in subcred_out.
* This can't fail. */
void
hs_get_subcredential(const ed25519_public_key_t *identity_pk,
const ed25519_public_key_t *blinded_pk,
- uint8_t *subcred_out)
+ hs_subcredential_t *subcred_out)
{
uint8_t credential[DIGEST256_LEN];
crypto_digest_t *digest;
@@ -841,7 +842,8 @@ hs_get_subcredential(const ed25519_public_key_t *identity_pk,
sizeof(credential));
crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
ED25519_PUBKEY_LEN);
- crypto_digest_get_digest(digest, (char *) subcred_out, DIGEST256_LEN);
+ crypto_digest_get_digest(digest, (char *) subcred_out->subcred,
+ SUBCRED_LEN);
crypto_digest_free(digest);
memwipe(credential, 0, sizeof(credential));
@@ -909,30 +911,35 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn)
* case the caller would want only one field. checksum_out MUST at least be 2
* bytes long.
*
- * Return 0 if parsing went well; return -1 in case of error. */
+ * Return 0 if parsing went well; return -1 in case of error and if errmsg is
+ * non NULL, a human readable string message is set. */
int
-hs_parse_address(const char *address, ed25519_public_key_t *key_out,
- uint8_t *checksum_out, uint8_t *version_out)
+hs_parse_address_no_log(const char *address, ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out,
+ const char **errmsg)
{
char decoded[HS_SERVICE_ADDR_LEN];
tor_assert(address);
+ if (errmsg) {
+ *errmsg = NULL;
+ }
+
/* Obvious length check. */
if (strlen(address) != HS_SERVICE_ADDR_LEN_BASE32) {
- log_warn(LD_REND, "Service address %s has an invalid length. "
- "Expected %lu but got %lu.",
- escaped_safe_str(address),
- (unsigned long) HS_SERVICE_ADDR_LEN_BASE32,
- (unsigned long) strlen(address));
+ if (errmsg) {
+ *errmsg = "Invalid length";
+ }
goto invalid;
}
/* Decode address so we can extract needed fields. */
if (base32_decode(decoded, sizeof(decoded), address, strlen(address))
!= sizeof(decoded)) {
- log_warn(LD_REND, "Service address %s can't be decoded.",
- escaped_safe_str(address));
+ if (errmsg) {
+ *errmsg = "Unable to base32 decode";
+ }
goto invalid;
}
@@ -944,6 +951,22 @@ hs_parse_address(const char *address, ed25519_public_key_t *key_out,
return -1;
}
+/** Same has hs_parse_address_no_log() but emits a log warning on parsing
+ * failure. */
+int
+hs_parse_address(const char *address, ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out)
+{
+ const char *errmsg = NULL;
+ int ret = hs_parse_address_no_log(address, key_out, checksum_out,
+ version_out, &errmsg);
+ if (ret < 0) {
+ log_warn(LD_REND, "Service address %s failed to be parsed: %s",
+ escaped_safe_str(address), errmsg);
+ }
+ return ret;
+}
+
/** Validate a given onion address. The length, the base32 decoding, and
* checksum are validated. Return 1 if valid else 0. */
int
@@ -1807,6 +1830,7 @@ hs_free_all(void)
hs_service_free_all();
hs_cache_free_all();
hs_client_free_all();
+ hs_ob_free_all();
}
/** For the given origin circuit circ, decrement the number of rendezvous
diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h
index 8f743d4d37..997b7298a6 100644
--- a/src/feature/hs/hs_common.h
+++ b/src/feature/hs/hs_common.h
@@ -179,6 +179,10 @@ void hs_build_address(const struct ed25519_public_key_t *key, uint8_t version,
int hs_address_is_valid(const char *address);
int hs_parse_address(const char *address, struct ed25519_public_key_t *key_out,
uint8_t *checksum_out, uint8_t *version_out);
+int hs_parse_address_no_log(const char *address,
+ struct ed25519_public_key_t *key_out,
+ uint8_t *checksum_out, uint8_t *version_out,
+ const char **errmsg);
void hs_build_blinded_pubkey(const struct ed25519_public_key_t *pubkey,
const uint8_t *secret, size_t secret_len,
@@ -210,9 +214,10 @@ const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
+struct hs_subcredential_t;
void hs_get_subcredential(const struct ed25519_public_key_t *identity_pk,
const struct ed25519_public_key_t *blinded_pk,
- uint8_t *subcred_out);
+ struct hs_subcredential_t *subcred_out);
uint64_t hs_get_previous_time_period_num(time_t now);
uint64_t hs_get_time_period_num(time_t now);
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index 64656b1935..684f7bc975 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -26,6 +26,7 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_client.h"
+#include "feature/hs/hs_ob.h"
#include "feature/hs/hs_service.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
@@ -219,6 +220,7 @@ config_has_invalid_options(const config_line_t *line_,
"HiddenServiceEnableIntroDoSDefense",
"HiddenServiceEnableIntroDoSRatePerSec",
"HiddenServiceEnableIntroDoSBurstPerSec",
+ "HiddenServiceOnionBalanceInstance",
NULL /* End marker. */
};
@@ -317,7 +319,7 @@ config_service_v3(const config_line_t *line_,
int have_num_ip = 0;
bool export_circuit_id = false; /* just to detect duplicate options */
bool dos_enabled = false, dos_rate_per_sec = false;
- bool dos_burst_per_sec = false;
+ bool dos_burst_per_sec = false, ob_instance = false;
const char *dup_opt_seen = NULL;
const config_line_t *line;
@@ -402,6 +404,27 @@ config_service_v3(const config_line_t *line_,
config->intro_dos_burst_per_sec);
continue;
}
+ if (!strcasecmp(line->key, "HiddenServiceOnionBalanceInstance")) {
+ bool enabled = !!helper_parse_uint64(line->key, line->value,
+ 0, 1, &ok);
+ if (!ok || ob_instance) {
+ if (ob_instance) {
+ dup_opt_seen = line->key;
+ }
+ goto err;
+ }
+ ob_instance = true;
+ if (!enabled) {
+ /* Skip if this is disabled. */
+ continue;
+ }
+ /* Option is enabled, parse config file. */
+ ok = hs_ob_parse_config_file(config);
+ if (!ok) {
+ goto err;
+ }
+ continue;
+ }
}
/* We do not load the key material for the service at this stage. This is
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c
index 65d6c7a581..c274ed7581 100644
--- a/src/feature/hs/hs_descriptor.c
+++ b/src/feature/hs/hs_descriptor.c
@@ -211,7 +211,7 @@ build_secret_input(const hs_descriptor_t *desc,
memcpy(secret_input, secret_data, secret_data_len);
offset += secret_data_len;
/* Copy subcredential. */
- memcpy(secret_input + offset, desc->subcredential, DIGEST256_LEN);
+ memcpy(secret_input + offset, desc->subcredential.subcred, DIGEST256_LEN);
offset += DIGEST256_LEN;
/* Copy revision counter value. */
set_uint64(secret_input + offset,
@@ -1018,10 +1018,6 @@ desc_encode_v3(const hs_descriptor_t *desc,
tor_assert(encoded_out);
tor_assert(desc->plaintext_data.version == 3);
- if (BUG(desc->subcredential == NULL)) {
- goto err;
- }
-
/* Build the non-encrypted values. */
{
char *encoded_cert;
@@ -1366,8 +1362,7 @@ encrypted_data_length_is_valid(size_t len)
* and return the buffer's length. The caller should wipe and free its content
* once done with it. This function can't fail. */
static size_t
-build_descriptor_cookie_keys(const uint8_t *subcredential,
- size_t subcredential_len,
+build_descriptor_cookie_keys(const hs_subcredential_t *subcredential,
const curve25519_secret_key_t *sk,
const curve25519_public_key_t *pk,
uint8_t **keys_out)
@@ -1389,7 +1384,7 @@ build_descriptor_cookie_keys(const uint8_t *subcredential,
/* Calculate KEYS = KDF(subcredential | SECRET_SEED, 40) */
xof = crypto_xof_new();
- crypto_xof_add_bytes(xof, subcredential, subcredential_len);
+ crypto_xof_add_bytes(xof, subcredential->subcred, SUBCRED_LEN);
crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed));
crypto_xof_squeeze_bytes(xof, keystream, keystream_len);
crypto_xof_free(xof);
@@ -1426,11 +1421,12 @@ decrypt_descriptor_cookie(const hs_descriptor_t *desc,
sizeof(desc->superencrypted_data.auth_ephemeral_pubkey)));
tor_assert(!fast_mem_is_zero((char *) client_auth_sk,
sizeof(*client_auth_sk)));
- tor_assert(!fast_mem_is_zero((char *) desc->subcredential, DIGEST256_LEN));
+ tor_assert(!fast_mem_is_zero((char *) desc->subcredential.subcred,
+ DIGEST256_LEN));
/* Get the KEYS component to derive the CLIENT-ID and COOKIE-KEY. */
keystream_length =
- build_descriptor_cookie_keys(desc->subcredential, DIGEST256_LEN,
+ build_descriptor_cookie_keys(&desc->subcredential,
client_auth_sk,
&desc->superencrypted_data.auth_ephemeral_pubkey,
&keystream);
@@ -2558,7 +2554,7 @@ hs_desc_decode_plaintext(const char *encoded,
* set to NULL. */
hs_desc_decode_status_t
hs_desc_decode_descriptor(const char *encoded,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
const curve25519_secret_key_t *client_auth_sk,
hs_descriptor_t **desc_out)
{
@@ -2576,7 +2572,7 @@ hs_desc_decode_descriptor(const char *encoded,
goto err;
}
- memcpy(desc->subcredential, subcredential, sizeof(desc->subcredential));
+ memcpy(&desc->subcredential, subcredential, sizeof(desc->subcredential));
ret = hs_desc_decode_plaintext(encoded, &desc->plaintext_data);
if (ret != HS_DESC_DECODE_OK) {
@@ -2666,7 +2662,7 @@ hs_desc_encode_descriptor,(const hs_descriptor_t *desc,
* symmetric only if the client auth is disabled. That is, the descriptor
* cookie will be NULL. */
if (!descriptor_cookie) {
- ret = hs_desc_decode_descriptor(*encoded_out, desc->subcredential,
+ ret = hs_desc_decode_descriptor(*encoded_out, &desc->subcredential,
NULL, NULL);
if (BUG(ret != HS_DESC_DECODE_OK)) {
ret = -1;
@@ -2870,7 +2866,7 @@ hs_desc_build_fake_authorized_client(void)
* key, and descriptor cookie, build the auth client so we can then encode the
* descriptor for publication. client_out must be already allocated. */
void
-hs_desc_build_authorized_client(const uint8_t *subcredential,
+hs_desc_build_authorized_client(const hs_subcredential_t *subcredential,
const curve25519_public_key_t *client_auth_pk,
const curve25519_secret_key_t *
auth_ephemeral_sk,
@@ -2898,7 +2894,7 @@ hs_desc_build_authorized_client(const uint8_t *subcredential,
/* Get the KEYS part so we can derive the CLIENT-ID and COOKIE-KEY. */
keystream_length =
- build_descriptor_cookie_keys(subcredential, DIGEST256_LEN,
+ build_descriptor_cookie_keys(subcredential,
auth_ephemeral_sk, client_auth_pk,
&keystream);
tor_assert(keystream_length > 0);
diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h
index 639dd31c8f..08daa904b6 100644
--- a/src/feature/hs/hs_descriptor.h
+++ b/src/feature/hs/hs_descriptor.h
@@ -14,6 +14,7 @@
#include "core/or/or.h"
#include "trunnel/ed25519_cert.h" /* needed for trunnel */
#include "feature/nodelist/torcert.h"
+#include "core/crypto/hs_ntor.h" /* for hs_subcredential_t */
/* Trunnel */
struct link_specifier_t;
@@ -238,7 +239,7 @@ typedef struct hs_descriptor_t {
/** Subcredentials of a service, used by the client and service to decrypt
* the encrypted data. */
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
} hs_descriptor_t;
/** Return true iff the given descriptor format version is supported. */
@@ -277,7 +278,7 @@ MOCK_DECL(int,
char **encoded_out));
int hs_desc_decode_descriptor(const char *encoded,
- const uint8_t *subcredential,
+ const hs_subcredential_t *subcredential,
const curve25519_secret_key_t *client_auth_sk,
hs_descriptor_t **desc_out);
int hs_desc_decode_plaintext(const char *encoded,
@@ -302,7 +303,7 @@ void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
hs_desc_authorized_client_t *hs_desc_build_fake_authorized_client(void);
-void hs_desc_build_authorized_client(const uint8_t *subcredential,
+void hs_desc_build_authorized_client(const hs_subcredential_t *subcredential,
const curve25519_public_key_t *
client_auth_pk,
const curve25519_secret_key_t *
diff --git a/src/feature/hs/hs_ob.c b/src/feature/hs/hs_ob.c
new file mode 100644
index 0000000000..c18a789013
--- /dev/null
+++ b/src/feature/hs/hs_ob.c
@@ -0,0 +1,408 @@
+/* Copyright (c) 2017-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_ob.c
+ * \brief Implement Onion Balance specific code.
+ **/
+
+#define HS_OB_PRIVATE
+
+#include "feature/hs/hs_service.h"
+
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/networkstatus_st.h"
+
+#include "lib/confmgt/confmgt.h"
+#include "lib/encoding/confline.h"
+
+#include "hs_ob.h"
+
+/* Options config magic number. */
+#define OB_OPTIONS_MAGIC 0x631DE7EA
+
+/* Helper macros. */
+#define VAR(varname, conftype, member, initvalue) \
+ CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue)
+#define V(member,conftype,initvalue) \
+ VAR(#member, conftype, member, initvalue)
+
+/* Dummy instance of ob_options_t, used for type-checking its members with
+ * CONF_CHECK_VAR_TYPE. */
+DUMMY_TYPECHECK_INSTANCE(ob_options_t);
+
+/* Array of variables for the config file options. */
+static const config_var_t config_vars[] = {
+ V(MasterOnionAddress, LINELIST, NULL),
+
+ END_OF_CONFIG_VARS
+};
+
+/* "Extra" variable in the state that receives lines we can't parse. This
+ * lets us preserve options from versions of Tor newer than us. */
+static const struct_member_t config_extra_vars = {
+ .name = "__extra",
+ .type = CONFIG_TYPE_LINELIST,
+ .offset = offsetof(ob_options_t, ExtraLines),
+};
+
+/* Configuration format of ob_options_t. */
+static const config_format_t config_format = {
+ .size = sizeof(ob_options_t),
+ .magic = {
+ "ob_options_t",
+ OB_OPTIONS_MAGIC,
+ offsetof(ob_options_t, magic_),
+ },
+ .vars = config_vars,
+ .extra = &config_extra_vars,
+};
+
+/* Global configuration manager for the config file. */
+static config_mgr_t *config_options_mgr = NULL;
+
+/* Return the configuration manager for the config file. */
+static const config_mgr_t *
+get_config_options_mgr(void)
+{
+ if (PREDICT_UNLIKELY(config_options_mgr == NULL)) {
+ config_options_mgr = config_mgr_new(&config_format);
+ config_mgr_freeze(config_options_mgr);
+ }
+ return config_options_mgr;
+}
+
+#define ob_option_free(val) \
+ FREE_AND_NULL(ob_options_t, ob_option_free_, (val))
+
+/** Helper: Free a config options object. */
+static void
+ob_option_free_(ob_options_t *opts)
+{
+ if (opts == NULL) {
+ return;
+ }
+ config_free(get_config_options_mgr(), opts);
+}
+
+/** Return an allocated config options object. */
+static ob_options_t *
+ob_option_new(void)
+{
+ ob_options_t *opts = config_new(get_config_options_mgr());
+ config_init(get_config_options_mgr(), opts);
+ return opts;
+}
+
+/** Helper function: From the configuration line value which is an onion
+ * address with the ".onion" extension, find the public key and put it in
+ * pkey_out.
+ *
+ * On success, true is returned. Else, false and pkey is untouched. */
+static bool
+get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
+{
+ char address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+ tor_assert(value);
+ tor_assert(pkey_out);
+
+ if (strcmpend(value, ".onion")) {
+ /* Not a .onion extension, bad format. */
+ return false;
+ }
+
+ /* Length validation. The -1 is because sizeof() counts the NUL byte. */
+ if (strlen(value) >
+ (HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
+ /* Too long, bad format. */
+ return false;
+ }
+
+ /* We don't want the .onion so we add 2 because size - 1 is copied with
+ * strlcpy() in order to accomodate the NUL byte and sizeof() counts the NUL
+ * byte so we need to remove them from the equation. */
+ strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
+
+ if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
+ return false;
+ }
+
+ /* Success. */
+ return true;
+}
+
+/** Parse the given ob options in opts and set the service config object
+ * accordingly.
+ *
+ * Return 1 on success else 0. */
+static int
+ob_option_parse(hs_service_config_t *config, const ob_options_t *opts)
+{
+ int ret = 0;
+ config_line_t *line;
+
+ tor_assert(config);
+ tor_assert(opts);
+
+ for (line = opts->MasterOnionAddress; line; line = line->next) {
+ /* Allocate config list if need be. */
+ if (!config->ob_master_pubkeys) {
+ config->ob_master_pubkeys = smartlist_new();
+ }
+ ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey));
+
+ if (!get_onion_public_key(line->value, pubkey)) {
+ log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid",
+ line->value);
+ tor_free(pubkey);
+ goto end;
+ }
+ smartlist_add(config->ob_master_pubkeys, pubkey);
+ log_info(LD_REND, "OnionBalance: MasterOnionAddress %s registered",
+ line->value);
+ }
+ /* Success. */
+ ret = 1;
+
+ end:
+ /* No keys added, we free the list since no list means no onion balance
+ * support for this tor instance. */
+ if (smartlist_len(config->ob_master_pubkeys) == 0) {
+ smartlist_free(config->ob_master_pubkeys);
+ }
+ return ret;
+}
+
+/** For the given master public key and time period, compute the subcredential
+ * and put them into subcredential. The subcredential parameter needs to be at
+ * least DIGEST256_LEN in size. */
+static void
+build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp,
+ hs_subcredential_t *subcredential)
+{
+ ed25519_public_key_t blinded_pubkey;
+
+ tor_assert(pkey);
+ tor_assert(subcredential);
+
+ hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey);
+ hs_get_subcredential(pkey, &blinded_pubkey, subcredential);
+}
+
+/*
+ * Public API.
+ */
+
+/** Return true iff the given service is configured as an onion balance
+ * instance. To satisfy that condition, there must at least be one master
+ * ed25519 public key configured. */
+bool
+hs_ob_service_is_instance(const hs_service_t *service)
+{
+ if (BUG(service == NULL)) {
+ return false;
+ }
+
+ /* No list, we are not an instance. */
+ if (!service->config.ob_master_pubkeys) {
+ return false;
+ }
+
+ return smartlist_len(service->config.ob_master_pubkeys) > 0;
+}
+
+/** Read and parse the config file at fname on disk. The service config object
+ * is populated with the options if any.
+ *
+ * Return 1 on success else 0. This is to follow the "ok" convention in
+ * hs_config.c. */
+int
+hs_ob_parse_config_file(hs_service_config_t *config)
+{
+ static const char *fname = "ob_config";
+ int ret = 0;
+ char *content = NULL, *errmsg = NULL, *config_file_path = NULL;
+ ob_options_t *options = NULL;
+ config_line_t *lines = NULL;
+
+ tor_assert(config);
+
+ /* Read file from disk. */
+ config_file_path = hs_path_from_filename(config->directory_path, fname);
+ content = read_file_to_str(config_file_path, 0, NULL);
+ if (!content) {
+ log_warn(LD_FS, "OnionBalance: Unable to read config file %s",
+ escaped(config_file_path));
+ goto end;
+ }
+
+ /* Parse lines. */
+ if (config_get_lines(content, &lines, 0) < 0) {
+ goto end;
+ }
+
+ options = ob_option_new();
+ config_assign(get_config_options_mgr(), options, lines, 0, &errmsg);
+ if (errmsg) {
+ log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s",
+ errmsg);
+ tor_free(errmsg);
+ goto end;
+ }
+
+ /* Parse the options and set the service config object with the details. */
+ ret = ob_option_parse(config, options);
+
+ end:
+ config_free_lines(lines);
+ ob_option_free(options);
+ tor_free(content);
+ tor_free(config_file_path);
+ return ret;
+}
+
+/** Compute all possible subcredentials for every onion master key in the given
+ * service config object. subcredentials_out is allocated and set as an
+ * continous array containing all possible values.
+ *
+ * On success, return the number of subcredential put in the array which will
+ * correspond to an arry of size: n * DIGEST256_LEN where DIGEST256_LEN is the
+ * length of a single subcredential.
+ *
+ * If the given configuration object has no OB master keys configured, 0 is
+ * returned and subcredentials_out is set to NULL.
+ *
+ * Otherwise, this can't fail. */
+STATIC size_t
+compute_subcredentials(const hs_service_t *service,
+ hs_subcredential_t **subcredentials_out)
+{
+ unsigned int num_pkeys, idx = 0;
+ hs_subcredential_t *subcreds = NULL;
+ const int steps[3] = {0, -1, 1};
+ const unsigned int num_steps = ARRAY_LENGTH(steps);
+ const uint64_t tp = hs_get_time_period_num(0);
+
+ tor_assert(service);
+ tor_assert(subcredentials_out);
+ /* Our caller has checked these too */
+ tor_assert(service->desc_current);
+ tor_assert(service->desc_next);
+
+ /* Make sure we are an OB instance, or bail out. */
+ num_pkeys = smartlist_len(service->config.ob_master_pubkeys);
+ if (!num_pkeys) {
+ *subcredentials_out = NULL;
+ return 0;
+ }
+
+ /* Time to build all the subcredentials for each time period: two for each
+ * instance descriptor plus three for the onionbalance frontend service: the
+ * previous one (-1), the current one (0) and the next one (1) for each
+ * configured key in order to accomodate client and service consensus skew.
+ *
+ * If the client consensus after_time is at 23:00 but the service one is at
+ * 01:00, the client will be using the previous time period where the
+ * service will think it is the client next time period. Thus why we have
+ * to try them all.
+ *
+ * The normal use case works because the service gets the descriptor object
+ * that corresponds to the intro point's request, and because each
+ * descriptor corresponds to a specific subcredential, we get the right
+ * subcredential out of it, and use that to do the decryption.
+ *
+ * As a slight optimization, statistically, the current time period (0) will
+ * be the one to work first so we'll put them first in the array to maximize
+ * our chance of success. */
+
+ /* We use a flat array, not a smartlist_t, in order to minimize memory
+ * allocation.
+ *
+ * Size of array is: length of a single subcredential multiplied by the
+ * number of time period we need to compute and finally multiplied by the
+ * total number of keys we are about to process. In other words, for each
+ * key, we allocate 3 subcredential slots. Then in the end we also add two
+ * subcredentials for this instance's active descriptors. */
+ subcreds =
+ tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t));
+
+ /* For each master pubkey we add 3 subcredentials: */
+ for (unsigned int i = 0; i < num_steps; i++) {
+ SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys,
+ const ed25519_public_key_t *, pkey) {
+ build_subcredential(pkey, tp + steps[i], &subcreds[idx]);
+ idx++;
+ } SMARTLIST_FOREACH_END(pkey);
+ }
+
+ /* And then in the end we add the two subcredentials of the current active
+ * instance descriptors */
+ memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential,
+ sizeof(hs_subcredential_t));
+ memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential,
+ sizeof(hs_subcredential_t));
+
+ log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).",
+ idx, (int)tp);
+
+ *subcredentials_out = subcreds;
+ return idx;
+}
+
+/**
+ * If we are an Onionbalance instance, refresh our keys.
+ *
+ * If we are not an Onionbalance instance or we are not ready to do so, this
+ * is a NOP.
+ *
+ * This function is called everytime we build a new descriptor. That's because
+ * we want our Onionbalance keys to always use up-to-date subcredentials both
+ * for the instance (ourselves) and for the onionbalance frontend.
+ */
+void
+hs_ob_refresh_keys(hs_service_t *service)
+{
+ hs_subcredential_t *ob_subcreds = NULL;
+ size_t num_subcreds;
+
+ tor_assert(service);
+
+ /* Don't do any of this if we are not configured as an OB instance */
+ if (!hs_ob_service_is_instance(service)) {
+ return;
+ }
+
+ /* We need both service descriptors created to make onionbalance keys.
+ *
+ * That's because we fetch our own (the instance's) subcredentials from our
+ * own descriptors which should always include the latest subcredentials that
+ * clients would use.
+ *
+ * This function is called with each descriptor build, so we will be
+ * eventually be called when both descriptors are created. */
+ if (!service->desc_current || !service->desc_next) {
+ return;
+ }
+
+ /* Get a new set of subcreds */
+ num_subcreds = compute_subcredentials(service, &ob_subcreds);
+ if (BUG(!num_subcreds)) {
+ return;
+ }
+
+ /* Delete old subcredentials if any */
+ if (service->ob_subcreds) {
+ tor_free(service->ob_subcreds);
+ }
+
+ service->ob_subcreds = ob_subcreds;
+ service->n_ob_subcreds = num_subcreds;
+}
+
+/** Free any memory allocated by the onionblance subsystem. */
+void
+hs_ob_free_all(void)
+{
+ config_mgr_free(config_options_mgr);
+}
diff --git a/src/feature/hs/hs_ob.h b/src/feature/hs/hs_ob.h
new file mode 100644
index 0000000000..d5b5504be7
--- /dev/null
+++ b/src/feature/hs/hs_ob.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file hs_ob.h
+ * \brief Header file for the specific code for onion balance.
+ **/
+
+#ifndef TOR_HS_OB_H
+#define TOR_HS_OB_H
+
+#include "hs_service.h"
+
+bool hs_ob_service_is_instance(const hs_service_t *service);
+
+int hs_ob_parse_config_file(hs_service_config_t *config);
+
+struct hs_subcredential_t;
+
+void hs_ob_free_all(void);
+
+void hs_ob_refresh_keys(hs_service_t *service);
+
+#ifdef HS_OB_PRIVATE
+
+STATIC size_t compute_subcredentials(const hs_service_t *service,
+ struct hs_subcredential_t **subcredentials);
+
+typedef struct ob_options_t {
+ /** Magic number to identify the structure in memory. */
+ uint32_t magic_;
+ /** Master Onion Address(es). */
+ struct config_line_t *MasterOnionAddress;
+ /** Extra Lines for configuration we might not know. */
+ struct config_line_t *ExtraLines;
+} ob_options_t;
+
+#endif /* defined(HS_OB_PRIVATE) */
+
+#endif /* !defined(TOR_HS_OB_H) */
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 81b37eab40..f0c791f21d 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -41,6 +41,7 @@
#include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_service.h"
#include "feature/hs/hs_stats.h"
+#include "feature/hs/hs_ob.h"
#include "feature/dircommon/dir_connection_st.h"
#include "core/or/edge_connection_st.h"
@@ -267,6 +268,11 @@ service_clear_config(hs_service_config_t *config)
service_authorized_client_free(p));
smartlist_free(config->clients);
}
+ if (config->ob_master_pubkeys) {
+ SMARTLIST_FOREACH(config->ob_master_pubkeys, ed25519_public_key_t *, k,
+ tor_free(k));
+ smartlist_free(config->ob_master_pubkeys);
+ }
memset(config, 0, sizeof(*config));
}
@@ -1764,7 +1770,8 @@ build_service_desc_superencrypted(const hs_service_t *service,
sizeof(curve25519_public_key_t));
/* Test that subcred is not zero because we might use it below */
- if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential, DIGEST256_LEN))) {
+ if (BUG(fast_mem_is_zero((char*)desc->desc->subcredential.subcred,
+ DIGEST256_LEN))) {
return -1;
}
@@ -1781,7 +1788,7 @@ build_service_desc_superencrypted(const hs_service_t *service,
/* Prepare the client for descriptor and then add to the list in the
* superencrypted part of the descriptor */
- hs_desc_build_authorized_client(desc->desc->subcredential,
+ hs_desc_build_authorized_client(&desc->desc->subcredential,
&client->client_pk,
&desc->auth_ephemeral_kp.seckey,
desc->descriptor_cookie, desc_client);
@@ -1837,7 +1844,7 @@ build_service_desc_plaintext(const hs_service_t *service,
/* Set the subcredential. */
hs_get_subcredential(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
- desc->desc->subcredential);
+ &desc->desc->subcredential);
plaintext = &desc->desc->plaintext_data;
@@ -1980,9 +1987,15 @@ build_service_descriptor(hs_service_t *service, uint64_t time_period_num,
/* Assign newly built descriptor to the next slot. */
*desc_out = desc;
+
/* Fire a CREATED control port event. */
hs_control_desc_event_created(service->onion_address,
&desc->blinded_kp.pubkey);
+
+ /* If we are an onionbalance instance, we refresh our keys when we rotate
+ * descriptors. */
+ hs_ob_refresh_keys(service);
+
return;
err:
@@ -3369,7 +3382,7 @@ service_handle_introduce2(origin_circuit_t *circ, const uint8_t *payload,
/* The following will parse, decode and launch the rendezvous point circuit.
* Both current and legacy cells are handled. */
- if (hs_circ_handle_introduce2(service, circ, ip, desc->desc->subcredential,
+ if (hs_circ_handle_introduce2(service, circ, ip, &desc->desc->subcredential,
payload, payload_len) < 0) {
goto err;
}
@@ -4042,6 +4055,11 @@ hs_service_free_(hs_service_t *service)
replaycache_free(service->state.replay_cache_rend_cookie);
}
+ /* Free onionbalance subcredentials (if any) */
+ if (service->ob_subcreds) {
+ tor_free(service->ob_subcreds);
+ }
+
/* Wipe service keys. */
memwipe(&service->keys.identity_sk, 0, sizeof(service->keys.identity_sk));
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index 8809411e01..94a73b2fec 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -248,10 +248,14 @@ typedef struct hs_service_config_t {
/** Does this service export the circuit ID of its clients? */
hs_circuit_id_protocol_t circuit_id_protocol;
- /* DoS defenses. For the ESTABLISH_INTRO cell extension. */
+ /** DoS defenses. For the ESTABLISH_INTRO cell extension. */
unsigned int has_dos_defense_enabled : 1;
uint32_t intro_dos_rate_per_sec;
uint32_t intro_dos_burst_per_sec;
+
+ /** If set, contains the Onion Balance master ed25519 public key (taken from
+ * an .onion addresses) that this tor instance serves as backend. */
+ smartlist_t *ob_master_pubkeys;
} hs_service_config_t;
/** Service state. */
@@ -301,8 +305,13 @@ typedef struct hs_service_t {
/** Next descriptor. */
hs_service_descriptor_t *desc_next;
- /* XXX: Credential (client auth.) #20700. */
-
+ /* If this is an onionbalance instance, this is an array of subcredentials
+ * that should be used when decrypting an INTRO2 cell. If this is not an
+ * onionbalance instance, this is NULL.
+ * See [ONIONBALANCE] section in rend-spec-v3.txt for more details . */
+ hs_subcredential_t *ob_subcreds;
+ /* Number of OB subcredentials */
+ size_t n_ob_subcreds;
} hs_service_t;
/** For the service global hash map, we define a specific type for it which
diff --git a/src/feature/hs/include.am b/src/feature/hs/include.am
index 5e69607e59..f83907c76b 100644
--- a/src/feature/hs/include.am
+++ b/src/feature/hs/include.am
@@ -13,6 +13,7 @@ LIBTOR_APP_A_SOURCES += \
src/feature/hs/hs_dos.c \
src/feature/hs/hs_ident.c \
src/feature/hs/hs_intropoint.c \
+ src/feature/hs/hs_ob.c \
src/feature/hs/hs_service.c \
src/feature/hs/hs_stats.c
@@ -30,6 +31,7 @@ noinst_HEADERS += \
src/feature/hs/hs_dos.h \
src/feature/hs/hs_ident.h \
src/feature/hs/hs_intropoint.h \
+ src/feature/hs/hs_ob.h \
src/feature/hs/hs_service.h \
src/feature/hs/hs_stats.h \
src/feature/hs/hsdir_index_st.h
diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c
index 06f4f5482b..ad3af0a143 100644
--- a/src/feature/nodelist/dirlist.c
+++ b/src/feature/nodelist/dirlist.c
@@ -49,6 +49,37 @@ static smartlist_t *trusted_dir_servers = NULL;
* and all fallback directory servers. */
static smartlist_t *fallback_dir_servers = NULL;
+/** Helper: From a given trusted directory entry, add the v4 or/and v6 address
+ * to the nodelist address set. */
+static void
+add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir)
+{
+ tor_assert(dir);
+ tor_assert(dir->is_authority);
+
+ /* Add IPv4 and then IPv6 if applicable. */
+ nodelist_add_addr4_to_address_set(dir->addr);
+ if (!tor_addr_is_null(&dir->ipv6_addr)) {
+ nodelist_add_addr6_to_address_set(&dir->ipv6_addr);
+ }
+}
+
+/** Go over the trusted directory server list and add their address(es) to the
+ * nodelist address set. This is called everytime a new consensus is set. */
+MOCK_IMPL(void,
+dirlist_add_trusted_dir_addresses, (void))
+{
+ if (!trusted_dir_servers) {
+ return;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, const dir_server_t *, ent) {
+ if (ent->is_authority) {
+ add_trusted_dir_to_nodelist_addr_set(ent);
+ }
+ } SMARTLIST_FOREACH_END(ent);
+}
+
/** Return the number of directory authorities whose type matches some bit set
* in <b>type</b> */
int
diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h
index 6bfc2e1b01..9201e76a9c 100644
--- a/src/feature/nodelist/dirlist.h
+++ b/src/feature/nodelist/dirlist.h
@@ -44,4 +44,6 @@ void dir_server_add(dir_server_t *ent);
void clear_dir_servers(void);
void dirlist_free_all(void);
+MOCK_DECL(void, dirlist_add_trusted_dir_addresses, (void));
+
#endif /* !defined(TOR_DIRLIST_H) */
diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c
index 94ff08826f..9bee16fcaf 100644
--- a/src/feature/nodelist/nodelist.c
+++ b/src/feature/nodelist/nodelist.c
@@ -455,22 +455,43 @@ node_add_to_address_set(const node_t *node)
if (node->rs) {
if (node->rs->addr)
- address_set_add_ipv4h(the_nodelist->node_addrs, node->rs->addr);
+ nodelist_add_addr4_to_address_set(node->rs->addr);
if (!tor_addr_is_null(&node->rs->ipv6_addr))
- address_set_add(the_nodelist->node_addrs, &node->rs->ipv6_addr);
+ nodelist_add_addr6_to_address_set(&node->rs->ipv6_addr);
}
if (node->ri) {
if (node->ri->addr)
- address_set_add_ipv4h(the_nodelist->node_addrs, node->ri->addr);
+ nodelist_add_addr4_to_address_set(node->ri->addr);
if (!tor_addr_is_null(&node->ri->ipv6_addr))
- address_set_add(the_nodelist->node_addrs, &node->ri->ipv6_addr);
+ nodelist_add_addr6_to_address_set(&node->ri->ipv6_addr);
}
if (node->md) {
if (!tor_addr_is_null(&node->md->ipv6_addr))
- address_set_add(the_nodelist->node_addrs, &node->md->ipv6_addr);
+ nodelist_add_addr6_to_address_set(&node->md->ipv6_addr);
}
}
+/** Add the given v4 address into the nodelist address set. */
+void
+nodelist_add_addr4_to_address_set(const uint32_t addr)
+{
+ if (!the_nodelist || !the_nodelist->node_addrs || addr == 0) {
+ return;
+ }
+ address_set_add_ipv4h(the_nodelist->node_addrs, addr);
+}
+
+/** Add the given v6 address into the nodelist address set. */
+void
+nodelist_add_addr6_to_address_set(const tor_addr_t *addr)
+{
+ if (BUG(!addr) || tor_addr_is_null(addr) || tor_addr_is_v4(addr) ||
+ !the_nodelist || !the_nodelist->node_addrs) {
+ return;
+ }
+ address_set_add(the_nodelist->node_addrs, addr);
+}
+
/** Return true if <b>addr</b> is the address of some node in the nodelist.
* If not, probably return false. */
int
@@ -612,9 +633,12 @@ nodelist_set_consensus(networkstatus_t *ns)
SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node,
node->rs = NULL);
- /* Conservatively estimate that every node will have 2 addresses. */
- const int estimated_addresses = smartlist_len(ns->routerstatus_list) *
- get_estimated_address_per_node();
+ /* Conservatively estimate that every node will have 2 addresses (v4 and
+ * v6). Then we add the number of configured trusted authorities we have. */
+ int estimated_addresses = smartlist_len(ns->routerstatus_list) *
+ get_estimated_address_per_node();
+ estimated_addresses += (get_n_authorities(V3_DIRINFO & BRIDGE_DIRINFO) *
+ get_estimated_address_per_node());
address_set_free(the_nodelist->node_addrs);
the_nodelist->node_addrs = address_set_new(estimated_addresses);
@@ -665,6 +689,9 @@ nodelist_set_consensus(networkstatus_t *ns)
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
node_add_to_address_set(node);
} SMARTLIST_FOREACH_END(node);
+ /* Then, add all trusted configured directories. Some might not be in the
+ * consensus so make sure we know them. */
+ dirlist_add_trusted_dir_addresses();
if (! authdir) {
SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) {
@@ -1213,8 +1240,8 @@ node_get_rsa_id_digest(const node_t *node)
* If node is NULL, returns an empty smartlist.
*
* The smartlist must be freed using link_specifier_smartlist_free(). */
-smartlist_t *
-node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+MOCK_IMPL(smartlist_t *,
+node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn))
{
link_specifier_t *ls;
tor_addr_port_t ap;
diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h
index 9742e3dff2..6e854ec879 100644
--- a/src/feature/nodelist/nodelist.h
+++ b/src/feature/nodelist/nodelist.h
@@ -35,6 +35,8 @@ node_t *nodelist_add_microdesc(microdesc_t *md);
void nodelist_set_consensus(networkstatus_t *ns);
void nodelist_ensure_freshness(networkstatus_t *ns);
int nodelist_probably_contains_address(const tor_addr_t *addr);
+void nodelist_add_addr4_to_address_set(const uint32_t addr);
+void nodelist_add_addr6_to_address_set(const tor_addr_t *addr);
void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md);
void nodelist_remove_routerinfo(routerinfo_t *ri);
@@ -78,8 +80,8 @@ int node_supports_ed25519_hs_intro(const node_t *node);
int node_supports_v3_rendezvous_point(const node_t *node);
int node_supports_establish_intro_dos_extension(const node_t *node);
const uint8_t *node_get_rsa_id_digest(const node_t *node);
-smartlist_t *node_get_link_specifier_smartlist(const node_t *node,
- bool direct_conn);
+MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node,
+ bool direct_conn));
void link_specifier_smartlist_free_(smartlist_t *ls_list);
#define link_specifier_smartlist_free(ls_list) \
FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list))
diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c
index da0cbb1df4..99f48ab2c2 100644
--- a/src/feature/relay/dns.c
+++ b/src/feature/relay/dns.c
@@ -268,22 +268,6 @@ has_dns_init_failed(void)
return nameserver_config_failed;
}
-/** Helper: Given a TTL from a DNS response, determine what TTL to give the
- * OP that asked us to resolve it, and how long to cache that record
- * ourselves. */
-uint32_t
-dns_clip_ttl(uint32_t ttl)
-{
- /* This logic is a defense against "DefectTor" DNS-based traffic
- * confirmation attacks, as in https://nymity.ch/tor-dns/tor-dns.pdf .
- * We only give two values: a "low" value and a "high" value.
- */
- if (ttl < MIN_DNS_TTL_AT_EXIT)
- return MIN_DNS_TTL_AT_EXIT;
- else
- return MAX_DNS_TTL_AT_EXIT;
-}
-
/** Helper: free storage held by an entry in the DNS cache. */
static void
free_cached_resolve_(cached_resolve_t *r)
@@ -521,7 +505,7 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type,
uint32_t ttl;
buf[0] = answer_type;
- ttl = dns_clip_ttl(conn->address_ttl);
+ ttl = clip_dns_ttl(conn->address_ttl);
switch (answer_type)
{
@@ -593,7 +577,7 @@ send_resolved_hostname_cell,(edge_connection_t *conn,
size_t namelen = strlen(hostname);
tor_assert(namelen < 256);
- ttl = dns_clip_ttl(conn->address_ttl);
+ ttl = clip_dns_ttl(conn->address_ttl);
buf[0] = RESOLVED_TYPE_HOSTNAME;
buf[1] = (uint8_t)namelen;
@@ -987,25 +971,6 @@ assert_connection_edge_not_dns_pending(edge_connection_t *conn)
#endif /* 1 */
}
-/** Log an error and abort if any connection waiting for a DNS resolve is
- * corrupted. */
-void
-assert_all_pending_dns_resolves_ok(void)
-{
- pending_connection_t *pend;
- cached_resolve_t **resolve;
-
- HT_FOREACH(resolve, cache_map, &cache_root) {
- for (pend = (*resolve)->pending_connections;
- pend;
- pend = pend->next) {
- assert_connection_ok(TO_CONN(pend->conn), 0);
- tor_assert(!SOCKET_OK(pend->conn->base_.s));
- tor_assert(!connection_in_array(TO_CONN(pend->conn)));
- }
- }
-}
-
/** Remove <b>conn</b> from the list of connections waiting for conn-\>address.
*/
void
@@ -1063,7 +1028,7 @@ connection_dns_remove(edge_connection_t *conn)
* the resolve for <b>address</b> itself, and remove any cached results for
* <b>address</b> from the cache.
*/
-MOCK_IMPL(void,
+MOCK_IMPL(STATIC void,
dns_cancel_pending_resolve,(const char *address))
{
pending_connection_t *pend;
@@ -1338,7 +1303,7 @@ make_pending_resolve_cached(cached_resolve_t *resolve)
resolve->ttl_hostname < ttl)
ttl = resolve->ttl_hostname;
- set_expiry(new_resolve, time(NULL) + dns_clip_ttl(ttl));
+ set_expiry(new_resolve, time(NULL) + clip_dns_ttl(ttl));
}
assert_cache_ok();
@@ -2188,7 +2153,7 @@ dns_cache_handle_oom(time_t now, size_t min_remove_bytes)
total_bytes_removed += bytes_removed;
/* Increase time_inc by a reasonable fraction. */
- time_inc += (MAX_DNS_TTL_AT_EXIT / 4);
+ time_inc += (MAX_DNS_TTL / 4);
} while (total_bytes_removed < min_remove_bytes);
return total_bytes_removed;
diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h
index 2b1da8d126..120b75bf8d 100644
--- a/src/feature/relay/dns.h
+++ b/src/feature/relay/dns.h
@@ -12,29 +12,14 @@
#ifndef TOR_DNS_H
#define TOR_DNS_H
-/** Lowest value for DNS ttl that a server will give. */
-#define MIN_DNS_TTL_AT_EXIT (5*60)
-/** Highest value for DNS ttl that a server will give. */
-#define MAX_DNS_TTL_AT_EXIT (60*60)
-
-/** How long do we keep DNS cache entries before purging them (regardless of
- * their TTL)? */
-#define MAX_DNS_ENTRY_AGE (3*60*60)
-/** How long do we cache/tell clients to cache DNS records when no TTL is
- * known? */
-#define DEFAULT_DNS_TTL (30*60)
+#ifdef HAVE_MODULE_RELAY
int dns_init(void);
int has_dns_init_failed(void);
-void dns_free_all(void);
-uint32_t dns_clip_ttl(uint32_t ttl);
int dns_reset(void);
void connection_dns_remove(edge_connection_t *conn);
void assert_connection_edge_not_dns_pending(edge_connection_t *conn);
-void assert_all_pending_dns_resolves_ok(void);
-MOCK_DECL(void,dns_cancel_pending_resolve,(const char *question));
int dns_resolve(edge_connection_t *exitconn);
-void dns_launch_correctness_checks(void);
int dns_seems_to_be_broken(void);
int dns_seems_to_be_broken_for_ipv6(void);
void dns_reset_correctness_checks(void);
@@ -42,6 +27,48 @@ size_t dns_cache_total_allocation(void);
void dump_dns_mem_usage(int severity);
size_t dns_cache_handle_oom(time_t now, size_t min_remove_bytes);
+/* These functions are only used within the feature/relay module, and don't
+ * need stubs. */
+void dns_free_all(void);
+void dns_launch_correctness_checks(void);
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define dns_init() (0)
+#define dns_seems_to_be_broken() (0)
+#define has_dns_init_failed() (0)
+#define dns_cache_total_allocation() (0)
+
+#define dns_reset_correctness_checks() STMT_NIL
+
+#define assert_connection_edge_not_dns_pending(conn) \
+ ((void)(conn))
+#define dump_dns_mem_usage(severity)\
+ ((void)(severity))
+#define dns_cache_handle_oom(now, bytes) \
+ ((void)(now), (void)(bytes), 0)
+
+#define connection_dns_remove(conn) \
+ STMT_BEGIN \
+ (void)(conn); \
+ tor_assert_nonfatal_unreached(); \
+ STMT_END
+
+static inline int
+dns_reset(void)
+{
+ return 0;
+}
+static inline int
+dns_resolve(edge_connection_t *exitconn)
+{
+ (void)exitconn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef DNS_PRIVATE
#include "feature/relay/dns_structs.h"
@@ -50,6 +77,7 @@ size_t number_of_configured_nameservers(void);
tor_addr_t *configured_nameserver_address(const size_t idx);
#endif
+MOCK_DECL(STATIC void,dns_cancel_pending_resolve,(const char *question));
MOCK_DECL(STATIC int,dns_resolve_impl,(edge_connection_t *exitconn,
int is_resolve,or_circuit_t *oncirc, char **hostname_out,
int *made_connection_pending_out, cached_resolve_t **resolve_out));
@@ -74,4 +102,3 @@ launch_resolve,(cached_resolve_t *resolve));
#endif /* defined(DNS_PRIVATE) */
#endif /* !defined(TOR_DNS_H) */
-
diff --git a/src/feature/relay/ext_orport.c b/src/feature/relay/ext_orport.c
index ce4e043dd7..5568dacf1a 100644
--- a/src/feature/relay/ext_orport.c
+++ b/src/feature/relay/ext_orport.c
@@ -652,6 +652,77 @@ connection_ext_or_start_auth(or_connection_t *or_conn)
return 0;
}
+/** Global map between Extended ORPort identifiers and OR
+ * connections. */
+static digestmap_t *orconn_ext_or_id_map = NULL;
+
+/** Remove the Extended ORPort identifier of <b>conn</b> from the
+ * global identifier list. Also, clear the identifier from the
+ * connection itself. */
+void
+connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
+{
+ or_connection_t *tmp;
+ if (!orconn_ext_or_id_map)
+ return;
+ if (!conn->ext_or_conn_id)
+ return;
+
+ tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
+ if (!tor_digest_is_zero(conn->ext_or_conn_id))
+ tor_assert(tmp == conn);
+
+ memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
+}
+
+#ifdef TOR_UNIT_TESTS
+/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
+ * connection is found. */
+or_connection_t *
+connection_or_get_by_ext_or_id(const char *id)
+{
+ if (!orconn_ext_or_id_map)
+ return NULL;
+ return digestmap_get(orconn_ext_or_id_map, id);
+}
+#endif /* defined(TOR_UNIT_TESTS) */
+
+/** Deallocate the global Extended ORPort identifier list */
+void
+connection_or_clear_ext_or_id_map(void)
+{
+ digestmap_free(orconn_ext_or_id_map, NULL);
+ orconn_ext_or_id_map = NULL;
+}
+
+/** Creates an Extended ORPort identifier for <b>conn</b> and deposits
+ * it into the global list of identifiers. */
+void
+connection_or_set_ext_or_identifier(or_connection_t *conn)
+{
+ char random_id[EXT_OR_CONN_ID_LEN];
+ or_connection_t *tmp;
+
+ if (!orconn_ext_or_id_map)
+ orconn_ext_or_id_map = digestmap_new();
+
+ /* Remove any previous identifiers: */
+ if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
+ connection_or_remove_from_ext_or_id_map(conn);
+
+ do {
+ crypto_rand(random_id, sizeof(random_id));
+ } while (digestmap_get(orconn_ext_or_id_map, random_id));
+
+ if (!conn->ext_or_conn_id)
+ conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
+
+ memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
+
+ tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
+ tor_assert(!tmp);
+}
+
/** Free any leftover allocated memory of the ext_orport.c subsystem. */
void
ext_orport_free_all(void)
diff --git a/src/feature/relay/ext_orport.h b/src/feature/relay/ext_orport.h
index dbe89fce18..416c358397 100644
--- a/src/feature/relay/ext_orport.h
+++ b/src/feature/relay/ext_orport.h
@@ -31,26 +31,56 @@
#define EXT_OR_CONN_STATE_FLUSHING 5
#define EXT_OR_CONN_STATE_MAX_ 5
-int connection_ext_or_start_auth(or_connection_t *or_conn);
-
-ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+#ifdef HAVE_MODULE_RELAY
-#define ext_or_cmd_free(cmd) \
- FREE_AND_NULL(ext_or_cmd_t, ext_or_cmd_free_, (cmd))
+int connection_ext_or_start_auth(or_connection_t *or_conn);
-void ext_or_cmd_free_(ext_or_cmd_t *cmd);
void connection_or_set_ext_or_identifier(or_connection_t *conn);
void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
void connection_or_clear_ext_or_id_map(void);
-or_connection_t *connection_or_get_by_ext_or_id(const char *id);
-
int connection_ext_or_finished_flushing(or_connection_t *conn);
int connection_ext_or_process_inbuf(or_connection_t *or_conn);
+char *get_ext_or_auth_cookie_file_name(void);
+/* (No stub needed for these: they are only called within feature/relay.) */
int init_ext_or_cookie_authentication(int is_enabled);
-char *get_ext_or_auth_cookie_file_name(void);
void ext_orport_free_all(void);
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+static inline int
+connection_ext_or_start_auth(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_ext_or_finished_flushing(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_ext_or_process_inbuf(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+#define connection_or_set_ext_or_identifier(conn) \
+ ((void)(conn))
+#define connection_or_remove_from_ext_or_id_map(conn) \
+ ((void)(conn))
+#define connection_or_clear_ext_or_id_map() \
+ STMT_NIL
+
+#define get_ext_or_auth_cookie_file_name() \
+ (NULL)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef EXT_ORPORT_PRIVATE
STATIC int connection_write_ext_or_command(connection_t *conn,
uint16_t command,
@@ -60,9 +90,11 @@ STATIC int handle_client_auth_nonce(const char *client_nonce,
size_t client_nonce_len,
char **client_hash_out,
char **reply_out, size_t *reply_len_out);
+
#ifdef TOR_UNIT_TESTS
extern uint8_t *ext_or_auth_cookie;
extern int ext_or_auth_cookie_is_set;
+or_connection_t *connection_or_get_by_ext_or_id(const char *id);
#endif
#endif /* defined(EXT_ORPORT_PRIVATE) */
diff --git a/src/feature/relay/include.am b/src/feature/relay/include.am
index a4c025ae12..813ddb8fb1 100644
--- a/src/feature/relay/include.am
+++ b/src/feature/relay/include.am
@@ -1,21 +1,22 @@
# Legacy shared relay code: migrate to the relay module over time
LIBTOR_APP_A_SOURCES += \
- src/feature/relay/dns.c \
- src/feature/relay/ext_orport.c \
src/feature/relay/onion_queue.c \
- src/feature/relay/router.c \
- src/feature/relay/routerkeys.c \
- src/feature/relay/selftest.c
+ src/feature/relay/router.c
# The Relay module.
# ADD_C_FILE: INSERT SOURCES HERE.
MODULE_RELAY_SOURCES = \
+ src/feature/relay/dns.c \
+ src/feature/relay/ext_orport.c \
src/feature/relay/routermode.c \
src/feature/relay/relay_config.c \
+ src/feature/relay/relay_handshake.c \
src/feature/relay/relay_periodic.c \
src/feature/relay/relay_sys.c \
+ src/feature/relay/routerkeys.c \
+ src/feature/relay/selftest.c \
src/feature/relay/transport_config.c
# ADD_C_FILE: INSERT HEADERS HERE.
@@ -25,6 +26,7 @@ noinst_HEADERS += \
src/feature/relay/ext_orport.h \
src/feature/relay/onion_queue.h \
src/feature/relay/relay_config.h \
+ src/feature/relay/relay_handshake.h \
src/feature/relay/relay_periodic.h \
src/feature/relay/relay_sys.h \
src/feature/relay/router.h \
diff --git a/src/feature/relay/relay_handshake.c b/src/feature/relay/relay_handshake.c
new file mode 100644
index 0000000000..030dc94956
--- /dev/null
+++ b/src/feature/relay/relay_handshake.c
@@ -0,0 +1,565 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_handshake.c
+ * @brief Functions to implement the relay-only parts of our
+ * connection handshake.
+ *
+ * Some parts of our TLS link handshake are only done by relays (including
+ * bridges). Specifically, only relays need to send CERTS cells; only
+ * relays need to send or receive AUTHCHALLENGE cells, and only relays need to
+ * send or receive AUTHENTICATE cells.
+ **/
+
+#include "orconfig.h"
+#include "core/or/or.h"
+#include "feature/relay/relay_handshake.h"
+
+#include "app/config/config.h"
+#include "core/or/connection_or.h"
+#include "lib/crypt_ops/crypto_rand.h"
+#include "trunnel/link_handshake.h"
+#include "feature/relay/routerkeys.h"
+#include "feature/nodelist/torcert.h"
+
+#include "core/or/or_connection_st.h"
+#include "core/or/or_handshake_certs_st.h"
+#include "core/or/or_handshake_state_st.h"
+#include "core/or/var_cell_st.h"
+
+#include "lib/tls/tortls.h"
+#include "lib/tls/x509.h"
+
+/** Helper used to add an encoded certs to a cert cell */
+static void
+add_certs_cell_cert_helper(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const uint8_t *cert_encoded,
+ size_t cert_len)
+{
+ tor_assert(cert_len <= UINT16_MAX);
+ certs_cell_cert_t *ccc = certs_cell_cert_new();
+ ccc->cert_type = cert_type;
+ ccc->cert_len = cert_len;
+ certs_cell_cert_setlen_body(ccc, cert_len);
+ memcpy(certs_cell_cert_getarray_body(ccc), cert_encoded, cert_len);
+
+ certs_cell_add_certs(certs_cell, ccc);
+}
+
+/** Add an encoded X509 cert (stored as <b>cert_len</b> bytes at
+ * <b>cert_encoded</b>) to the trunnel certs_cell_t object that we are
+ * building in <b>certs_cell</b>. Set its type field to <b>cert_type</b>.
+ * (If <b>cert</b> is NULL, take no action.) */
+static void
+add_x509_cert(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const tor_x509_cert_t *cert)
+{
+ if (NULL == cert)
+ return;
+
+ const uint8_t *cert_encoded = NULL;
+ size_t cert_len;
+ tor_x509_cert_get_der(cert, &cert_encoded, &cert_len);
+
+ add_certs_cell_cert_helper(certs_cell, cert_type, cert_encoded, cert_len);
+}
+
+/** Add an Ed25519 cert from <b>cert</b> to the trunnel certs_cell_t object
+ * that we are building in <b>certs_cell</b>. Set its type field to
+ * <b>cert_type</b>. (If <b>cert</b> is NULL, take no action.) */
+static void
+add_ed25519_cert(certs_cell_t *certs_cell,
+ uint8_t cert_type,
+ const tor_cert_t *cert)
+{
+ if (NULL == cert)
+ return;
+
+ add_certs_cell_cert_helper(certs_cell, cert_type,
+ cert->encoded, cert->encoded_len);
+}
+
+#ifdef TOR_UNIT_TESTS
+int certs_cell_ed25519_disabled_for_testing = 0;
+#else
+#define certs_cell_ed25519_disabled_for_testing 0
+#endif
+
+/** Send a CERTS cell on the connection <b>conn</b>. Return 0 on success, -1
+ * on failure. */
+int
+connection_or_send_certs_cell(or_connection_t *conn)
+{
+ const tor_x509_cert_t *global_link_cert = NULL, *id_cert = NULL;
+ tor_x509_cert_t *own_link_cert = NULL;
+ var_cell_t *cell;
+
+ certs_cell_t *certs_cell = NULL;
+
+ tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+
+ const int conn_in_server_mode = ! conn->handshake_state->started_here;
+
+ /* Get the encoded values of the X509 certificates */
+ if (tor_tls_get_my_certs(conn_in_server_mode,
+ &global_link_cert, &id_cert) < 0)
+ return -1;
+
+ if (conn_in_server_mode) {
+ own_link_cert = tor_tls_get_own_cert(conn->tls);
+ }
+ tor_assert(id_cert);
+
+ certs_cell = certs_cell_new();
+
+ /* Start adding certs. First the link cert or auth1024 cert. */
+ if (conn_in_server_mode) {
+ tor_assert_nonfatal(own_link_cert);
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_TLS_LINK, own_link_cert);
+ } else {
+ tor_assert(global_link_cert);
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_AUTH_1024, global_link_cert);
+ }
+
+ /* Next the RSA->RSA ID cert */
+ add_x509_cert(certs_cell,
+ OR_CERT_TYPE_ID_1024, id_cert);
+
+ /* Next the Ed25519 certs */
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_ID_SIGN,
+ get_master_signing_key_cert());
+ if (conn_in_server_mode) {
+ tor_assert_nonfatal(conn->handshake_state->own_link_cert ||
+ certs_cell_ed25519_disabled_for_testing);
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_SIGN_LINK,
+ conn->handshake_state->own_link_cert);
+ } else {
+ add_ed25519_cert(certs_cell,
+ CERTTYPE_ED_SIGN_AUTH,
+ get_current_auth_key_cert());
+ }
+
+ /* And finally the crosscert. */
+ {
+ const uint8_t *crosscert=NULL;
+ size_t crosscert_len;
+ get_master_rsa_crosscert(&crosscert, &crosscert_len);
+ if (crosscert) {
+ add_certs_cell_cert_helper(certs_cell,
+ CERTTYPE_RSA1024_ID_EDID,
+ crosscert, crosscert_len);
+ }
+ }
+
+ /* We've added all the certs; make the cell. */
+ certs_cell->n_certs = certs_cell_getlen_certs(certs_cell);
+
+ ssize_t alloc_len = certs_cell_encoded_len(certs_cell);
+ tor_assert(alloc_len >= 0 && alloc_len <= UINT16_MAX);
+ cell = var_cell_new(alloc_len);
+ cell->command = CELL_CERTS;
+ ssize_t enc_len = certs_cell_encode(cell->payload, alloc_len, certs_cell);
+ tor_assert(enc_len > 0 && enc_len <= alloc_len);
+ cell->payload_len = enc_len;
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+ certs_cell_free(certs_cell);
+ tor_x509_cert_free(own_link_cert);
+
+ return 0;
+}
+
+#ifdef TOR_UNIT_TESTS
+int testing__connection_or_pretend_TLSSECRET_is_supported = 0;
+#else
+#define testing__connection_or_pretend_TLSSECRET_is_supported 0
+#endif
+
+/** Return true iff <b>challenge_type</b> is an AUTHCHALLENGE type that
+ * we can send and receive. */
+int
+authchallenge_type_is_supported(uint16_t challenge_type)
+{
+ switch (challenge_type) {
+ case AUTHTYPE_RSA_SHA256_TLSSECRET:
+#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS
+ return 1;
+#else
+ return testing__connection_or_pretend_TLSSECRET_is_supported;
+#endif
+ case AUTHTYPE_ED25519_SHA256_RFC5705:
+ return 1;
+ case AUTHTYPE_RSA_SHA256_RFC5705:
+ default:
+ return 0;
+ }
+}
+
+/** Return true iff <b>challenge_type_a</b> is one that we would rather
+ * use than <b>challenge_type_b</b>. */
+int
+authchallenge_type_is_better(uint16_t challenge_type_a,
+ uint16_t challenge_type_b)
+{
+ /* Any supported type is better than an unsupported one;
+ * all unsupported types are equally bad. */
+ if (!authchallenge_type_is_supported(challenge_type_a))
+ return 0;
+ if (!authchallenge_type_is_supported(challenge_type_b))
+ return 1;
+ /* It happens that types are superior in numerically ascending order.
+ * If that ever changes, this must change too. */
+ return (challenge_type_a > challenge_type_b);
+}
+
+/** Send an AUTH_CHALLENGE cell on the connection <b>conn</b>. Return 0
+ * on success, -1 on failure. */
+int
+connection_or_send_auth_challenge_cell(or_connection_t *conn)
+{
+ var_cell_t *cell = NULL;
+ int r = -1;
+ tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
+
+ if (! conn->handshake_state)
+ return -1;
+
+ auth_challenge_cell_t *ac = auth_challenge_cell_new();
+
+ tor_assert(sizeof(ac->challenge) == 32);
+ crypto_rand((char*)ac->challenge, sizeof(ac->challenge));
+
+ if (authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET))
+ auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
+ /* Disabled, because everything that supports this method also supports
+ * the much-superior ED25519_SHA256_RFC5705 */
+ /* auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_RFC5705); */
+ if (authchallenge_type_is_supported(AUTHTYPE_ED25519_SHA256_RFC5705))
+ auth_challenge_cell_add_methods(ac, AUTHTYPE_ED25519_SHA256_RFC5705);
+ auth_challenge_cell_set_n_methods(ac,
+ auth_challenge_cell_getlen_methods(ac));
+
+ cell = var_cell_new(auth_challenge_cell_encoded_len(ac));
+ ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len,
+ ac);
+ if (len != cell->payload_len) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Encoded auth challenge cell length not as expected");
+ goto done;
+ /* LCOV_EXCL_STOP */
+ }
+ cell->command = CELL_AUTH_CHALLENGE;
+
+ connection_or_write_var_cell_to_buf(cell, conn);
+ r = 0;
+
+ done:
+ var_cell_free(cell);
+ auth_challenge_cell_free(ac);
+
+ return r;
+}
+
+/** Compute the main body of an AUTHENTICATE cell that a client can use
+ * to authenticate itself on a v3 handshake for <b>conn</b>. Return it
+ * in a var_cell_t.
+ *
+ * If <b>server</b> is true, only calculate the first
+ * V3_AUTH_FIXED_PART_LEN bytes -- the part of the authenticator that's
+ * determined by the rest of the handshake, and which match the provided value
+ * exactly.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is NULL, calculate the
+ * first V3_AUTH_BODY_LEN bytes of the authenticator (that is, everything
+ * that should be signed), but don't actually sign it.
+ *
+ * If <b>server</b> is false and <b>signing_key</b> is provided, calculate the
+ * entire authenticator, signed with <b>signing_key</b>.
+ *
+ * Return the length of the cell body on success, and -1 on failure.
+ */
+var_cell_t *
+connection_or_compute_authenticate_cell_body(or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const ed25519_keypair_t *ed_signing_key,
+ int server)
+{
+ auth1_t *auth = NULL;
+ auth_ctx_t *ctx = auth_ctx_new();
+ var_cell_t *result = NULL;
+ int old_tlssecrets_algorithm = 0;
+ const char *authtype_str = NULL;
+
+ int is_ed = 0;
+
+ /* assert state is reasonable XXXX */
+ switch (authtype) {
+ case AUTHTYPE_RSA_SHA256_TLSSECRET:
+ authtype_str = "AUTH0001";
+ old_tlssecrets_algorithm = 1;
+ break;
+ case AUTHTYPE_RSA_SHA256_RFC5705:
+ authtype_str = "AUTH0002";
+ break;
+ case AUTHTYPE_ED25519_SHA256_RFC5705:
+ authtype_str = "AUTH0003";
+ is_ed = 1;
+ break;
+ default:
+ tor_assert(0);
+ break;
+ }
+
+ auth = auth1_new();
+ ctx->is_ed = is_ed;
+
+ /* Type: 8 bytes. */
+ memcpy(auth1_getarray_type(auth), authtype_str, 8);
+
+ {
+ const tor_x509_cert_t *id_cert=NULL;
+ const common_digests_t *my_digests, *their_digests;
+ const uint8_t *my_id, *their_id, *client_id, *server_id;
+ if (tor_tls_get_my_certs(server, NULL, &id_cert))
+ goto err;
+ my_digests = tor_x509_cert_get_id_digests(id_cert);
+ their_digests =
+ tor_x509_cert_get_id_digests(conn->handshake_state->certs->id_cert);
+ tor_assert(my_digests);
+ tor_assert(their_digests);
+ my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
+ their_id = (uint8_t*)their_digests->d[DIGEST_SHA256];
+
+ client_id = server ? their_id : my_id;
+ server_id = server ? my_id : their_id;
+
+ /* Client ID digest: 32 octets. */
+ memcpy(auth->cid, client_id, 32);
+
+ /* Server ID digest: 32 octets. */
+ memcpy(auth->sid, server_id, 32);
+ }
+
+ if (is_ed) {
+ const ed25519_public_key_t *my_ed_id, *their_ed_id;
+ if (!conn->handshake_state->certs->ed_id_sign) {
+ log_warn(LD_OR, "Ed authenticate without Ed ID cert from peer.");
+ goto err;
+ }
+ my_ed_id = get_master_identity_key();
+ their_ed_id = &conn->handshake_state->certs->ed_id_sign->signing_key;
+
+ const uint8_t *cid_ed = (server ? their_ed_id : my_ed_id)->pubkey;
+ const uint8_t *sid_ed = (server ? my_ed_id : their_ed_id)->pubkey;
+
+ memcpy(auth->u1_cid_ed, cid_ed, ED25519_PUBKEY_LEN);
+ memcpy(auth->u1_sid_ed, sid_ed, ED25519_PUBKEY_LEN);
+ }
+
+ {
+ crypto_digest_t *server_d, *client_d;
+ if (server) {
+ server_d = conn->handshake_state->digest_sent;
+ client_d = conn->handshake_state->digest_received;
+ } else {
+ client_d = conn->handshake_state->digest_sent;
+ server_d = conn->handshake_state->digest_received;
+ }
+
+ /* Server log digest : 32 octets */
+ crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
+
+ /* Client log digest : 32 octets */
+ crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
+ }
+
+ {
+ /* Digest of cert used on TLS link : 32 octets. */
+ tor_x509_cert_t *cert = NULL;
+ if (server) {
+ cert = tor_tls_get_own_cert(conn->tls);
+ } else {
+ cert = tor_tls_get_peer_cert(conn->tls);
+ }
+ if (!cert) {
+ log_warn(LD_OR, "Unable to find cert when making %s data.",
+ authtype_str);
+ goto err;
+ }
+
+ memcpy(auth->scert,
+ tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
+
+ tor_x509_cert_free(cert);
+ }
+
+ /* HMAC of clientrandom and serverrandom using master key : 32 octets */
+ if (old_tlssecrets_algorithm) {
+ if (tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets) < 0) {
+ log_fn(LOG_PROTOCOL_WARN, LD_OR, "Somebody asked us for an older TLS "
+ "authentication method (AUTHTYPE_RSA_SHA256_TLSSECRET) "
+ "which we don't support.");
+ }
+ } else {
+ char label[128];
+ tor_snprintf(label, sizeof(label),
+ "EXPORTER FOR TOR TLS CLIENT BINDING %s", authtype_str);
+ int r = tor_tls_export_key_material(conn->tls, auth->tlssecrets,
+ auth->cid, sizeof(auth->cid),
+ label);
+ if (r < 0) {
+ if (r != -2)
+ log_warn(LD_BUG, "TLS key export failed for unknown reason.");
+ // If r == -2, this was openssl bug 7712.
+ goto err;
+ }
+ }
+
+ /* 8 octets were reserved for the current time, but we're trying to get out
+ * of the habit of sending time around willynilly. Fortunately, nothing
+ * checks it. That's followed by 16 bytes of nonce. */
+ crypto_rand((char*)auth->rand, 24);
+
+ ssize_t maxlen = auth1_encoded_len(auth, ctx);
+ if (ed_signing_key && is_ed) {
+ maxlen += ED25519_SIG_LEN;
+ } else if (signing_key && !is_ed) {
+ maxlen += crypto_pk_keysize(signing_key);
+ }
+
+ const int AUTH_CELL_HEADER_LEN = 4; /* 2 bytes of type, 2 bytes of length */
+ result = var_cell_new(AUTH_CELL_HEADER_LEN + maxlen);
+ uint8_t *const out = result->payload + AUTH_CELL_HEADER_LEN;
+ const size_t outlen = maxlen;
+ ssize_t len;
+
+ result->command = CELL_AUTHENTICATE;
+ set_uint16(result->payload, htons(authtype));
+
+ if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to encode signed part of AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (server) {
+ auth1_t *tmp = NULL;
+ ssize_t len2 = auth1_parse(&tmp, out, len, ctx);
+ if (!tmp) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to parse signed part of AUTH1 data that "
+ "we just encoded");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ result->payload_len = (tmp->end_of_signed - result->payload);
+
+ auth1_free(tmp);
+ if (len2 != len) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Mismatched length when re-parsing AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ goto done;
+ }
+
+ if (ed_signing_key && is_ed) {
+ ed25519_signature_t sig;
+ if (ed25519_sign(&sig, out, len, ed_signing_key) < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to sign ed25519 authentication data");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ auth1_setlen_sig(auth, ED25519_SIG_LEN);
+ memcpy(auth1_getarray_sig(auth), sig.sig, ED25519_SIG_LEN);
+
+ } else if (signing_key && !is_ed) {
+ auth1_setlen_sig(auth, crypto_pk_keysize(signing_key));
+
+ char d[32];
+ crypto_digest256(d, (char*)out, len, DIGEST_SHA256);
+ int siglen = crypto_pk_private_sign(signing_key,
+ (char*)auth1_getarray_sig(auth),
+ auth1_getlen_sig(auth),
+ d, 32);
+ if (siglen < 0) {
+ log_warn(LD_OR, "Unable to sign AUTH1 data.");
+ goto err;
+ }
+
+ auth1_setlen_sig(auth, siglen);
+ }
+
+ len = auth1_encode(out, outlen, auth, ctx);
+ if (len < 0) {
+ /* LCOV_EXCL_START */
+ log_warn(LD_BUG, "Unable to encode signed AUTH1 data.");
+ goto err;
+ /* LCOV_EXCL_STOP */
+ }
+ tor_assert(len + AUTH_CELL_HEADER_LEN <= result->payload_len);
+ result->payload_len = len + AUTH_CELL_HEADER_LEN;
+ set_uint16(result->payload+2, htons(len));
+
+ goto done;
+
+ err:
+ var_cell_free(result);
+ result = NULL;
+ done:
+ auth1_free(auth);
+ auth_ctx_free(ctx);
+ return result;
+}
+
+/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
+ * success, -1 on failure */
+MOCK_IMPL(int,
+connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype))
+{
+ var_cell_t *cell;
+ crypto_pk_t *pk = tor_tls_get_my_client_auth_key();
+ /* XXXX make sure we're actually supposed to send this! */
+
+ if (!pk) {
+ log_warn(LD_BUG, "Can't compute authenticate cell: no client auth key");
+ return -1;
+ }
+ if (! authchallenge_type_is_supported(authtype)) {
+ log_warn(LD_BUG, "Tried to send authenticate cell with unknown "
+ "authentication type %d", authtype);
+ return -1;
+ }
+
+ cell = connection_or_compute_authenticate_cell_body(conn,
+ authtype,
+ pk,
+ get_current_auth_keypair(),
+ 0 /* not server */);
+ if (! cell) {
+ log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unable to compute authenticate cell!");
+ return -1;
+ }
+ connection_or_write_var_cell_to_buf(cell, conn);
+ var_cell_free(cell);
+
+ return 0;
+}
diff --git a/src/feature/relay/relay_handshake.h b/src/feature/relay/relay_handshake.h
new file mode 100644
index 0000000000..99a658cbcc
--- /dev/null
+++ b/src/feature/relay/relay_handshake.h
@@ -0,0 +1,90 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file relay_handshake.h
+ * @brief Header for feature/relay/relay_handshake.c
+ **/
+
+#ifndef TOR_CORE_OR_RELAY_HANDSHAKE_H
+#define TOR_CORE_OR_RELAY_HANDSHAKE_H
+
+#ifdef HAVE_MODULE_RELAY
+struct ed25519_keypair_t;
+
+int connection_or_send_certs_cell(or_connection_t *conn);
+int connection_or_send_auth_challenge_cell(or_connection_t *conn);
+
+var_cell_t *connection_or_compute_authenticate_cell_body(
+ or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const struct ed25519_keypair_t *ed_signing_key,
+ int server);
+
+int authchallenge_type_is_supported(uint16_t challenge_type);
+int authchallenge_type_is_better(uint16_t challenge_type_a,
+ uint16_t challenge_type_b);
+
+MOCK_DECL(int,connection_or_send_authenticate_cell,
+ (or_connection_t *conn, int type));
+
+#ifdef TOR_UNIT_TESTS
+extern int certs_cell_ed25519_disabled_for_testing;
+#endif
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+static inline int
+connection_or_send_certs_cell(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+static inline int
+connection_or_send_auth_challenge_cell(or_connection_t *conn)
+{
+ (void)conn;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+static inline var_cell_t *
+connection_or_compute_authenticate_cell_body(
+ or_connection_t *conn,
+ const int authtype,
+ crypto_pk_t *signing_key,
+ const struct ed25519_keypair_t *ed_signing_key,
+ int server)
+{
+ (void)conn;
+ (void)authtype;
+ (void)signing_key;
+ (void)ed_signing_key;
+ (void)server;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+
+#define authchallenge_type_is_supported(t) (0)
+#define authchallenge_type_is_better(a, b) (0)
+
+static inline int
+connection_or_send_authenticate_cell(or_connection_t *conn, int type)
+{
+ (void)conn;
+ (void)type;
+ tor_assert_nonfatal_unreached();
+ return -1;
+}
+
+#ifdef TOR_UNIT_TESTS
+extern int certs_cell_ed25519_disabled_for_testing;
+#endif
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
+#endif /* !defined(TOR_CORE_OR_RELAY_HANDSHAKE_H) */
diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h
index 782609d8ab..2e07df2e88 100644
--- a/src/feature/relay/router.h
+++ b/src/feature/relay/router.h
@@ -117,7 +117,6 @@ const char *routerinfo_err_to_string(int err);
int routerinfo_err_is_transient(int err);
void router_reset_warnings(void);
-void router_reset_reachability(void);
void router_free_all(void);
#ifdef ROUTER_PRIVATE
diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h
index c2475f195f..2b5f03a2a3 100644
--- a/src/feature/relay/routerkeys.h
+++ b/src/feature/relay/routerkeys.h
@@ -11,6 +11,8 @@
#include "lib/crypt_ops/crypto_ed25519.h"
+#ifdef HAVE_MODULE_RELAY
+
const ed25519_public_key_t *get_master_identity_key(void);
MOCK_DECL(const ed25519_keypair_t *, get_master_signing_keypair,(void));
MOCK_DECL(const struct tor_cert_st *, get_master_signing_key_cert,(void));
@@ -24,6 +26,7 @@ void get_master_rsa_crosscert(const uint8_t **cert_out,
int router_ed25519_id_is_me(const ed25519_public_key_t *id);
+/* These are only used by router.c */
struct tor_cert_st *make_ntor_onion_key_crosscert(
const curve25519_keypair_t *onion_key,
const ed25519_public_key_t *master_id_key,
@@ -42,6 +45,85 @@ int generate_ed_link_cert(const or_options_t *options, time_t now, int force);
void routerkeys_free_all(void);
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define router_ed25519_id_is_me(id) \
+ ((void)(id), 0)
+
+static inline void *
+relay_key_is_unavailable_(void)
+{
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+#define relay_key_is_unavailable(type) \
+ ((type)(relay_key_is_unavailable_()))
+
+// Many of these can be removed once relay_handshake.c is relay-only.
+#define get_current_auth_keypair() \
+ relay_key_is_unavailable(const ed25519_keypair_t *)
+#define get_master_signing_keypair() \
+ relay_key_is_unavailable(const ed25519_keypair_t *)
+#define get_current_link_cert_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_current_auth_key_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_master_signing_key_cert() \
+ relay_key_is_unavailable(const struct tor_cert_st *)
+#define get_master_rsa_crosscert(cert_out, size_out) \
+ STMT_BEGIN \
+ tor_assert_nonfatal_unreached(); \
+ *(cert_out) = NULL; \
+ *(size_out) = 0; \
+ STMT_END
+#define get_master_identity_key() \
+ relay_key_is_unavailable(const ed25519_public_key_t *)
+
+#define generate_ed_link_cert(options, now, force) \
+ ((void)(options), (void)(now), (void)(force), 0)
+#define should_make_new_ed_keys(options, now) \
+ ((void)(options), (void)(now), 0)
+
+// These can get removed once router.c becomes relay-only.
+static inline struct tor_cert_st *
+make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ time_t now, time_t lifetime,
+ int *sign_out)
+{
+ (void)onion_key;
+ (void)master_id_key;
+ (void)now;
+ (void)lifetime;
+ (void)sign_out;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+static inline uint8_t *
+make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ const crypto_pk_t *rsa_id_key,
+ int *len_out)
+{
+ (void)onion_key;
+ (void)master_id_key;
+ (void)rsa_id_key;
+ (void)len_out;
+ tor_assert_nonfatal_unreached();
+ return NULL;
+}
+
+/* This calls is used outside of relay mode, but only to implement
+ * CMD_KEY_EXPIRATION */
+#define log_cert_expiration() \
+ (puts("Not available: Tor has been compiled without relay support"), 0)
+/* This calls is used outside of relay mode, but only to implement
+ * CMD_KEYGEN. */
+#define load_ed_keys(x,y) \
+ (puts("Not available: Tor has been compiled without relay support"), 0)
+
+#endif /* defined(HAVE_MODULE_RELAY) */
+
#ifdef TOR_UNIT_TESTS
const ed25519_keypair_t *get_master_identity_keypair(void);
void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key);
diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h
index 94f305f203..f3dd698bb7 100644
--- a/src/feature/relay/selftest.h
+++ b/src/feature/relay/selftest.h
@@ -12,6 +12,7 @@
#ifndef TOR_SELFTEST_H
#define TOR_SELFTEST_H
+#ifdef HAVE_MODULE_RELAY
struct or_options_t;
int check_whether_orport_reachable(const struct or_options_t *options);
int check_whether_dirport_reachable(const struct or_options_t *options);
@@ -20,5 +21,37 @@ void router_do_reachability_checks(int test_or, int test_dir);
void router_orport_found_reachable(void);
void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now);
+void router_reset_reachability(void);
+
+#else /* !defined(HAVE_MODULE_RELAY) */
+
+#define check_whether_orport_reachable(opts) \
+ ((void)(opts), 0)
+#define check_whether_dirport_reachable(opts) \
+ ((void)(opts), 0)
+
+#define router_orport_found_reachable() \
+ STMT_NIL
+#define router_dirport_found_reachable() \
+ STMT_NIL
+#define router_reset_reachability() \
+ STMT_NIL
+
+static inline void
+router_do_reachability_checks(int test_or, int test_dir)
+{
+ (void)test_or;
+ (void)test_dir;
+ tor_assert_nonfatal_unreached();
+}
+static inline void
+router_perform_bandwidth_test(int num_circs, time_t now)
+{
+ (void)num_circs;
+ (void)now;
+ tor_assert_nonfatal_unreached();
+}
+
+#endif /* defined(HAVE_MODULE_RELAY) */
#endif /* !defined(TOR_SELFTEST_H) */
diff --git a/src/include.am b/src/include.am
index f5f868d23f..657f6e823a 100644
--- a/src/include.am
+++ b/src/include.am
@@ -19,6 +19,7 @@ include src/lib/fs/include.am
include src/lib/geoip/include.am
include src/lib/include.libdonna.am
include src/lib/intmath/include.am
+include src/lib/llharden/include.am
include src/lib/lock/include.am
include src/lib/log/include.am
include src/lib/math/include.am
diff --git a/src/lib/compress/compress.c b/src/lib/compress/compress.c
index c62d7d5d2a..84e9601920 100644
--- a/src/lib/compress/compress.c
+++ b/src/lib/compress/compress.c
@@ -695,6 +695,6 @@ subsys_compress_initialize(void)
const subsys_fns_t sys_compress = {
.name = "compress",
.supported = true,
- .level = -70,
+ .level = -55,
.initialize = subsys_compress_initialize,
};
diff --git a/src/lib/ctime/di_ops.c b/src/lib/ctime/di_ops.c
index 7448a9973e..e1ac0943fb 100644
--- a/src/lib/ctime/di_ops.c
+++ b/src/lib/ctime/di_ops.c
@@ -279,3 +279,30 @@ select_array_member_cumulative_timei(const uint64_t *entries, int n_entries,
return i_chosen;
}
+
+/**
+ * If <b>s</b> is true, then copy <b>n</b> bytes from <b>src</b> to
+ * <b>dest</b>. Otherwise leave <b>dest</b> alone.
+ *
+ * This function behaves the same as
+ *
+ * if (s)
+ * memcpy(dest, src, n);
+ *
+ * except that it tries to run in the same amount of time whether <b>s</b> is
+ * true or not.
+ **/
+void
+memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n)
+{
+ // If s is true, mask will be ~0. If s is false, mask will be 0.
+ const char mask = (char) -(signed char)s;
+
+ char *destp = dest;
+ const char *srcp = src;
+ for (size_t i = 0; i < n; ++i) {
+ *destp = (*destp & ~mask) | (*srcp & mask);
+ ++destp;
+ ++srcp;
+ }
+}
diff --git a/src/lib/ctime/di_ops.h b/src/lib/ctime/di_ops.h
index 4ff8f03165..9fe2884ecc 100644
--- a/src/lib/ctime/di_ops.h
+++ b/src/lib/ctime/di_ops.h
@@ -73,4 +73,6 @@ int select_array_member_cumulative_timei(const uint64_t *entries,
int n_entries,
uint64_t total, uint64_t rand_val);
+void memcpy_if_true_timei(bool s, void *dest, const void *src, size_t n);
+
#endif /* !defined(TOR_DI_OPS_H) */
diff --git a/src/lib/llharden/.may_include b/src/lib/llharden/.may_include
new file mode 100644
index 0000000000..038237dadf
--- /dev/null
+++ b/src/lib/llharden/.may_include
@@ -0,0 +1,3 @@
+lib/llharden/*.h
+lib/subsys/*.h
+orconfig.h
diff --git a/src/lib/llharden/include.am b/src/lib/llharden/include.am
new file mode 100644
index 0000000000..0a4788c7dc
--- /dev/null
+++ b/src/lib/llharden/include.am
@@ -0,0 +1,19 @@
+
+noinst_LIBRARIES += src/lib/libtor-llharden.a
+
+if UNITTESTS_ENABLED
+noinst_LIBRARIES += src/lib/libtor-llharden-testing.a
+endif
+
+# ADD_C_FILE: INSERT SOURCES HERE.
+src_lib_libtor_llharden_a_SOURCES = \
+ src/lib/llharden/winprocess_sys.c
+
+src_lib_libtor_llharden_testing_a_SOURCES = \
+ $(src_lib_libtor_llharden_a_SOURCES)
+src_lib_libtor_llharden_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_lib_libtor_llharden_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+
+# ADD_C_FILE: INSERT HEADERS HERE.
+noinst_HEADERS += \
+ src/lib/llharden/winprocess_sys.h
diff --git a/src/lib/llharden/lib_llharden.md b/src/lib/llharden/lib_llharden.md
new file mode 100644
index 0000000000..69e9af5327
--- /dev/null
+++ b/src/lib/llharden/lib_llharden.md
@@ -0,0 +1,6 @@
+@dir /lib/llharden
+@brief lib/llharden: low-level unconditional process hardening
+
+This module contains process hardening code that we want to run before any
+other code, including configuration. It needs to be self-contained, since
+nothing else will be initialized at this point.
diff --git a/src/lib/process/winprocess_sys.c b/src/lib/llharden/winprocess_sys.c
index e43a77e467..a5f22c182b 100644
--- a/src/lib/process/winprocess_sys.c
+++ b/src/lib/llharden/winprocess_sys.c
@@ -8,7 +8,7 @@
#include "orconfig.h"
#include "lib/subsys/subsys.h"
-#include "lib/process/winprocess_sys.h"
+#include "lib/llharden/winprocess_sys.h"
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/lib/process/winprocess_sys.h b/src/lib/llharden/winprocess_sys.h
index bece1b3da9..bece1b3da9 100644
--- a/src/lib/process/winprocess_sys.h
+++ b/src/lib/llharden/winprocess_sys.h
diff --git a/src/lib/net/network_sys.c b/src/lib/net/network_sys.c
index 012fc56bba..f0421385b7 100644
--- a/src/lib/net/network_sys.c
+++ b/src/lib/net/network_sys.c
@@ -39,7 +39,7 @@ const subsys_fns_t sys_network = {
.name = "network",
/* Network depends on logging, and a lot of other modules depend on network.
*/
- .level = -80,
+ .level = -55,
.supported = true,
.initialize = subsys_network_initialize,
.shutdown = subsys_network_shutdown,
diff --git a/src/lib/process/include.am b/src/lib/process/include.am
index af5f99617b..18876b3f54 100644
--- a/src/lib/process/include.am
+++ b/src/lib/process/include.am
@@ -16,8 +16,7 @@ src_lib_libtor_process_a_SOURCES = \
src/lib/process/process_win32.c \
src/lib/process/restrict.c \
src/lib/process/setuid.c \
- src/lib/process/waitpid.c \
- src/lib/process/winprocess_sys.c
+ src/lib/process/waitpid.c
src_lib_libtor_process_testing_a_SOURCES = \
$(src_lib_libtor_process_a_SOURCES)
@@ -35,5 +34,4 @@ noinst_HEADERS += \
src/lib/process/process_win32.h \
src/lib/process/restrict.h \
src/lib/process/setuid.h \
- src/lib/process/waitpid.h \
- src/lib/process/winprocess_sys.h
+ src/lib/process/waitpid.h
diff --git a/src/lib/process/process_sys.c b/src/lib/process/process_sys.c
index 283064cbfe..015ffadead 100644
--- a/src/lib/process/process_sys.c
+++ b/src/lib/process/process_sys.c
@@ -26,7 +26,7 @@ subsys_process_shutdown(void)
const subsys_fns_t sys_process = {
.name = "process",
- .level = -35,
+ .level = -18,
.supported = true,
.initialize = subsys_process_initialize,
.shutdown = subsys_process_shutdown
diff --git a/src/lib/thread/compat_threads.c b/src/lib/thread/compat_threads.c
index d56e8a3f76..21125bddad 100644
--- a/src/lib/thread/compat_threads.c
+++ b/src/lib/thread/compat_threads.c
@@ -130,8 +130,6 @@ subsys_threads_initialize(void)
const subsys_fns_t sys_threads = {
.name = "threads",
.supported = true,
- /* Threads is used by logging, which is a diagnostic feature, we want it to
- * init right after low-level error handling and approx time. */
- .level = -95,
+ .level = -89,
.initialize = subsys_threads_initialize,
};
diff --git a/src/test/fuzz/fuzz_hsdescv3.c b/src/test/fuzz/fuzz_hsdescv3.c
index 3955241389..8d7eab1a8d 100644
--- a/src/test/fuzz/fuzz_hsdescv3.c
+++ b/src/test/fuzz/fuzz_hsdescv3.c
@@ -85,12 +85,12 @@ int
fuzz_main(const uint8_t *data, size_t sz)
{
hs_descriptor_t *desc = NULL;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
char *fuzzing_data = tor_memdup_nulterm(data, sz);
- memset(subcredential, 'A', sizeof(subcredential));
+ memset(&subcredential, 'A', sizeof(subcredential));
- hs_desc_decode_descriptor(fuzzing_data, subcredential, NULL, &desc);
+ hs_desc_decode_descriptor(fuzzing_data, &subcredential, NULL, &desc);
if (desc) {
log_debug(LD_GENERAL, "Decoding okay");
hs_descriptor_free(desc);
@@ -101,4 +101,3 @@ fuzz_main(const uint8_t *data, size_t sz)
tor_free(fuzzing_data);
return 0;
}
-
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index e8b99aaac8..5116fc7169 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -13,9 +13,22 @@
#include "feature/hs/hs_service.h"
#include "test/hs_test_helpers.h"
+/**
+ * Create an introduction point taken straight out of an HSv3 descriptor.
+ *
+ * Use 'signing_kp' to sign the introduction point certificates.
+ *
+ * If 'intro_auth_kp' is provided use that as the introduction point
+ * authentication keypair, otherwise generate one on the fly.
+ *
+ * If 'intro_enc_kp' is provided use that as the introduction point encryption
+ * keypair, otherwise generate one on the fly.
+ */
hs_desc_intro_point_t *
hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
- const char *addr, int legacy)
+ const char *addr, int legacy,
+ const ed25519_keypair_t *intro_auth_kp,
+ const curve25519_keypair_t *intro_enc_kp)
{
int ret;
ed25519_keypair_t auth_kp;
@@ -56,8 +69,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
smartlist_add(ip->link_specifiers, ls_ip);
}
- ret = ed25519_keypair_generate(&auth_kp, 0);
- tt_int_op(ret, OP_EQ, 0);
+ if (intro_auth_kp) {
+ memcpy(&auth_kp, intro_auth_kp, sizeof(ed25519_keypair_t));
+ } else {
+ ret = ed25519_keypair_generate(&auth_kp, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ }
ip->auth_key_cert = tor_cert_create(signing_kp, CERT_TYPE_AUTH_HS_IP_KEY,
&auth_kp.pubkey, now,
HS_DESC_CERT_LIFETIME,
@@ -85,8 +102,12 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
ed25519_keypair_t ed25519_kp;
tor_cert_t *cross_cert;
- ret = curve25519_keypair_generate(&curve25519_kp, 0);
- tt_int_op(ret, OP_EQ, 0);
+ if (intro_enc_kp) {
+ memcpy(&curve25519_kp, intro_enc_kp, sizeof(curve25519_keypair_t));
+ } else {
+ ret = curve25519_keypair_generate(&curve25519_kp, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ }
ed25519_keypair_from_curve25519_keypair(&ed25519_kp, &signbit,
&curve25519_kp);
cross_cert = tor_cert_create(signing_kp, CERT_TYPE_CROSS_HS_IP_KEYS,
@@ -95,6 +116,8 @@ hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
CERT_FLAG_INCLUDE_SIGNING_KEY);
tt_assert(cross_cert);
ip->enc_key_cert = cross_cert;
+ memcpy(ip->enc_key.public_key, curve25519_kp.pubkey.public_key,
+ CURVE25519_PUBKEY_LEN);
}
intro_point = ip;
@@ -140,7 +163,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
desc->plaintext_data.lifetime_sec = 3 * 60 * 60;
hs_get_subcredential(&signing_kp->pubkey, &blinded_kp.pubkey,
- desc->subcredential);
+ &desc->subcredential);
/* Setup superencrypted data section. */
ret = curve25519_keypair_generate(&auth_ephemeral_kp, 0);
@@ -165,13 +188,17 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
if (!no_ip) {
/* Add four intro points. */
smartlist_add(desc->encrypted_data.intro_points,
- hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0));
+ hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0,
+ NULL, NULL));
smartlist_add(desc->encrypted_data.intro_points,
- hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0));
+ hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0,
+ NULL, NULL));
smartlist_add(desc->encrypted_data.intro_points,
- hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
+ hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1,
+ NULL, NULL));
smartlist_add(desc->encrypted_data.intro_points,
- hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1));
+ hs_helper_build_intro_point(signing_kp, now, "5.6.7.8", 1,
+ NULL, NULL));
}
descp = desc;
@@ -186,7 +213,7 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
* an HS. Used to decrypt descriptors in unittests. */
void
hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
- uint8_t *subcred_out)
+ hs_subcredential_t *subcred_out)
{
ed25519_keypair_t blinded_kp;
uint64_t current_time_period = hs_get_time_period_num(approx_time());
@@ -233,7 +260,7 @@ hs_helper_build_hs_desc_with_client_auth(
memcpy(&desc->superencrypted_data.auth_ephemeral_pubkey,
&auth_ephemeral_kp.pubkey, sizeof(curve25519_public_key_t));
- hs_desc_build_authorized_client(desc->subcredential, client_pk,
+ hs_desc_build_authorized_client(&desc->subcredential, client_pk,
&auth_ephemeral_kp.seckey,
descriptor_cookie, desc_client);
smartlist_add(desc->superencrypted_data.clients, desc_client);
diff --git a/src/test/hs_test_helpers.h b/src/test/hs_test_helpers.h
index a01fd45d63..23d11f2a4a 100644
--- a/src/test/hs_test_helpers.h
+++ b/src/test/hs_test_helpers.h
@@ -8,9 +8,11 @@
#include "feature/hs/hs_descriptor.h"
/* Set of functions to help build and test descriptors. */
-hs_desc_intro_point_t *hs_helper_build_intro_point(
- const ed25519_keypair_t *signing_kp, time_t now,
- const char *addr, int legacy);
+hs_desc_intro_point_t *
+hs_helper_build_intro_point(const ed25519_keypair_t *signing_kp, time_t now,
+ const char *addr, int legacy,
+ const ed25519_keypair_t *intro_auth_kp,
+ const curve25519_keypair_t *intro_enc_kp);
hs_descriptor_t *hs_helper_build_hs_desc_no_ip(
const ed25519_keypair_t *signing_kp);
hs_descriptor_t *hs_helper_build_hs_desc_with_ip(
@@ -21,12 +23,11 @@ hs_descriptor_t *hs_helper_build_hs_desc_with_client_auth(
const ed25519_keypair_t *signing_kp);
void hs_helper_desc_equal(const hs_descriptor_t *desc1,
const hs_descriptor_t *desc2);
-void
-hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
- uint8_t *subcred_out);
+struct hs_subcredential_t;
+void hs_helper_get_subcred_from_identity_keypair(ed25519_keypair_t *signing_kp,
+ struct hs_subcredential_t *subcred_out);
void hs_helper_add_client_auth(const ed25519_public_key_t *service_pk,
const curve25519_secret_key_t *client_sk);
#endif /* !defined(TOR_HS_TEST_HELPERS_H) */
-
diff --git a/src/test/include.am b/src/test/include.am
index fdd1195fdf..de927836d6 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -66,18 +66,20 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
# this test only uses IPv4
TEST_CHUTNEY_FLAVOR_QUICK = bridges+hs-v23
# only run if we can ping6 ::1 (localhost)
-TEST_CHUTNEY_FLAVOR_QUICK_IPV6 = client-ipv6-only-md
+TEST_CHUTNEY_FLAVOR_QUICK_IPV6 = single-onion-v23-ipv6-md
# run a basic set of tests, which only use IPv4
-TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \
- single-onion-v23
+TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v23-min single-onion-v23
-# only run if we can ping6 ::1 (localhost)
+# only run if we can ping ::1 (localhost)
TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6-md \
single-onion-v23-ipv6-md
# only run if we can find a stable (or simply another) version of tor
-TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v2
+TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v23
+
+# only run if IPv6 and mixed networks are run
+TEST_CHUTNEY_FLAVORS_IPV6_MIXED = mixed+hs-v23-ipv6
### This is a lovely feature, but it requires automake >= 1.12, and Tor
### doesn't require that yet.
@@ -177,6 +179,7 @@ src_test_test_SOURCES += \
src/test/test_hs_client.c \
src/test/test_hs_intropoint.c \
src/test/test_hs_control.c \
+ src/test/test_hs_ob.c \
src/test/test_handles.c \
src/test/test_hs_cache.c \
src/test/test_hs_descriptor.c \
diff --git a/src/test/test.c b/src/test/test.c
index 1742f1d952..4b6082ce4f 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -721,6 +721,7 @@ struct testgroup_t testgroups[] = {
{ "hs_dos/", hs_dos_tests },
{ "hs_intropoint/", hs_intropoint_tests },
{ "hs_ntor/", hs_ntor_tests },
+ { "hs_ob/", hs_ob_tests },
{ "hs_service/", hs_service_tests },
{ "introduce/", introduce_tests },
{ "keypin/", keypin_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 63e2faff95..18987719d0 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -141,6 +141,7 @@ extern struct testcase_t hs_descriptor[];
extern struct testcase_t hs_dos_tests[];
extern struct testcase_t hs_intropoint_tests[];
extern struct testcase_t hs_ntor_tests[];
+extern struct testcase_t hs_ob_tests[];
extern struct testcase_t hs_service_tests[];
extern struct testcase_t hs_tests[];
extern struct testcase_t introduce_tests[];
diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c
index 3fcf8c4d13..829ecd79e8 100644
--- a/src/test/test_address_set.c
+++ b/src/test/test_address_set.c
@@ -4,6 +4,7 @@
#include "core/or/or.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "core/or/address_set.h"
+#include "feature/nodelist/dirlist.h"
#include "feature/nodelist/microdesc.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -31,6 +32,12 @@ mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
return dummy_ns;
}
+static void
+mock_dirlist_add_trusted_dir_addresses(void)
+{
+ return;
+}
+
/* Number of address a single node_t can have. Default to the production
* value. This is to control the size of the bloom filter. */
static int addr_per_node = 2;
@@ -98,6 +105,8 @@ test_nodelist(void *arg)
mock_networkstatus_get_latest_consensus_by_flavor);
MOCK(get_estimated_address_per_node,
mock_get_estimated_address_per_node);
+ MOCK(dirlist_add_trusted_dir_addresses,
+ mock_dirlist_add_trusted_dir_addresses);
dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
dummy_ns->flavor = FLAV_MICRODESC;
@@ -113,7 +122,10 @@ test_nodelist(void *arg)
* (the_nodelist->node_addrs) so we will fail the contain test rarely. */
addr_per_node = 1024;
- /* No node no nothing. The lookups should be empty. */
+ /* No node no nothing. The lookups should be empty. We've mocked the
+ * dirlist_add_trusted_dir_addresses in order for _no_ authorities to be
+ * added to the filter else it makes this test to trigger many false
+ * positive. */
nodelist_set_consensus(dummy_ns);
/* The address set should be empty. */
@@ -167,6 +179,7 @@ test_nodelist(void *arg)
UNMOCK(networkstatus_get_latest_consensus);
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(get_estimated_address_per_node);
+ UNMOCK(dirlist_add_trusted_dir_addresses);
}
struct testcase_t address_set_tests[] = {
diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c
index 1da3796981..117783cafc 100644
--- a/src/test/test_bwmgt.c
+++ b/src/test/test_bwmgt.c
@@ -6,18 +6,70 @@
* \brief tests for bandwidth management / token bucket functions
*/
+#define CONFIG_PRIVATE
+#define CONNECTION_PRIVATE
+#define DIRAUTH_SYS_PRIVATE
#define TOKEN_BUCKET_PRIVATE
#include "core/or/or.h"
-#include "test/test.h"
+#include "app/config/config.h"
+#include "core/mainloop/connection.h"
+#include "feature/dirauth/dirauth_sys.h"
+#include "feature/dircommon/directory.h"
+#include "feature/nodelist/microdesc.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/nodelist.h"
+#include "feature/nodelist/routerlist.h"
+#include "lib/crypt_ops/crypto_rand.h"
#include "lib/evloop/token_bucket.h"
+#include "test/test.h"
+#include "test/test_helpers.h"
+
+#include "app/config/or_options_st.h"
+#include "core/or/connection_st.h"
+#include "feature/dirauth/dirauth_options_st.h"
+#include "feature/nodelist/microdesc_st.h"
+#include "feature/nodelist/networkstatus_st.h"
+#include "feature/nodelist/routerinfo_st.h"
+#include "feature/nodelist/routerstatus_st.h"
// an imaginary time, in timestamp units. Chosen so it will roll over.
static const uint32_t START_TS = UINT32_MAX-10;
static const int32_t KB = 1024;
static const uint32_t GB = (UINT64_C(1) << 30);
+static or_options_t mock_options;
+
+static const or_options_t *
+mock_get_options(void)
+{
+ return &mock_options;
+}
+
+static networkstatus_t *dummy_ns = NULL;
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ return dummy_ns;
+}
+
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
+{
+ tor_assert(f == FLAV_MICRODESC);
+ return dummy_ns;
+}
+
+/* Number of address a single node_t can have. Default to the production
+ * value. This is to control the size of the bloom filter. */
+static int addr_per_node = 2;
+static int
+mock_get_estimated_address_per_node(void)
+{
+ return addr_per_node;
+}
+
static void
test_bwmgt_token_buf_init(void *arg)
{
@@ -220,8 +272,167 @@ test_bwmgt_token_buf_helpers(void *arg)
;
}
+static void
+test_bwmgt_dir_conn_global_write_low(void *arg)
+{
+ bool ret;
+ int addr_family;
+ connection_t *conn = NULL;
+ routerstatus_t *rs = NULL; microdesc_t *md = NULL; routerinfo_t *ri = NULL;
+ tor_addr_t relay_addr;
+ dirauth_options_t *dirauth_opts = NULL;
+
+ (void) arg;
+
+ memset(&mock_options, 0, sizeof(or_options_t));
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ MOCK(networkstatus_get_latest_consensus_by_flavor,
+ mock_networkstatus_get_latest_consensus_by_flavor);
+ MOCK(get_estimated_address_per_node,
+ mock_get_estimated_address_per_node);
+
+ /*
+ * The following is rather complex but that is what it takes to add a dummy
+ * consensus with a valid routerlist which will populate our node address
+ * set that we need to lookup to test the known relay code path.
+ *
+ * We MUST do that before we MOCK(get_options) else it is another world of
+ * complexity.
+ */
+
+ /* This will be the address of our relay. */
+ tor_addr_parse(&relay_addr, "1.2.3.4");
+
+ /* We'll now add a relay into our routerlist and see if we let it. */
+ dummy_ns = tor_malloc_zero(sizeof(*dummy_ns));
+ dummy_ns->flavor = FLAV_MICRODESC;
+ dummy_ns->routerstatus_list = smartlist_new();
+
+ md = tor_malloc_zero(sizeof(*md));
+ ri = tor_malloc_zero(sizeof(*ri));
+ rs = tor_malloc_zero(sizeof(*rs));
+ crypto_rand(rs->identity_digest, sizeof(rs->identity_digest));
+ crypto_rand(md->digest, sizeof(md->digest));
+ memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN);
+
+ /* Set IP address. */
+ rs->addr = tor_addr_to_ipv4h(&relay_addr);
+ ri->addr = rs->addr;
+ /* Add the rs to the consensus becoming a node_t. */
+ smartlist_add(dummy_ns->routerstatus_list, rs);
+
+ /* Add all configured authorities (hardcoded) before we set the consensus so
+ * the address set exists. */
+ ret = consider_adding_dir_servers(&mock_options, &mock_options);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* This will make the nodelist bloom filter very large
+ * (the_nodelist->node_addrs) so we will fail the contain test rarely. */
+ addr_per_node = 1024;
+
+ nodelist_set_consensus(dummy_ns);
+
+ dirauth_opts = tor_malloc_zero(sizeof(dirauth_options_t));
+ dirauth_opts->AuthDirRejectRequestsUnderLoad = 0;
+ dirauth_set_options(dirauth_opts);
+
+ /* Ok, now time to control which options we use. */
+ MOCK(get_options, mock_get_options);
+
+ /* Set ourselves as an authoritative dir. */
+ mock_options.AuthoritativeDir = 1;
+ mock_options.V3AuthoritativeDir = 1;
+ mock_options.UseDefaultFallbackDirs = 0;
+
+ /* This will set our global bucket to 1 byte and thus we will hit the
+ * banwdith limit in our test. */
+ mock_options.BandwidthRate = 1;
+ mock_options.BandwidthBurst = 1;
+
+ /* Else an IPv4 address screams. */
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Initialize the global buckets. */
+ connection_bucket_init();
+
+ /* The address "127.0.0.1" is set with this helper. */
+ conn = test_conn_get_connection(DIR_CONN_STATE_MIN_, CONN_TYPE_DIR,
+ DIR_PURPOSE_MIN_);
+ tt_assert(conn);
+
+ /* First try a non authority non relay IP thus a client but we are not
+ * configured to reject requests under load so we should get a false value
+ * that our limit is _not_ low. */
+ addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Now, we will reject requests under load so try again a non authority non
+ * relay IP thus a client. We should get a warning that our limit is too
+ * low. */
+ dirauth_opts->AuthDirRejectRequestsUnderLoad = 1;
+
+ addr_family = tor_addr_parse(&conn->addr, "1.1.1.1");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ /* Now, lets try with a connection address from moria1. It should always
+ * pass even though our limit is too low. */
+ addr_family = tor_addr_parse(&conn->addr, "128.31.0.39");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* IPv6 testing of gabelmoo. */
+ addr_family = tor_addr_parse(&conn->addr, "[2001:638:a000:4140::ffff:189]");
+ tt_int_op(addr_family, OP_EQ, AF_INET6);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Lets retry with a known relay address. It should pass. Possible due to
+ * our consensus setting above. */
+ memcpy(&conn->addr, &relay_addr, sizeof(tor_addr_t));
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Lets retry with a random IP that is not an authority nor a relay. */
+ addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Finally, just make sure it still denies an IP if we are _not_ a v3
+ * directory authority. */
+ mock_options.V3AuthoritativeDir = 0;
+ addr_family = tor_addr_parse(&conn->addr, "1.2.3.4");
+ tt_int_op(addr_family, OP_EQ, AF_INET);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ /* Random IPv6 should not be allowed. */
+ addr_family = tor_addr_parse(&conn->addr, "[CAFE::ACAB]");
+ tt_int_op(addr_family, OP_EQ, AF_INET6);
+ ret = connection_dir_is_global_write_low(conn, INT_MAX);
+ tt_int_op(ret, OP_EQ, 1);
+
+ done:
+ connection_free_minimal(conn);
+ routerstatus_free(rs); routerinfo_free(ri); microdesc_free(md);
+ smartlist_clear(dummy_ns->routerstatus_list);
+ networkstatus_vote_free(dummy_ns);
+
+ UNMOCK(get_estimated_address_per_node);
+ UNMOCK(networkstatus_get_latest_consensus);
+ UNMOCK(networkstatus_get_latest_consensus_by_flavor);
+ UNMOCK(get_options);
+}
+
#define BWMGT(name) \
- { #name, test_bwmgt_ ## name , 0, NULL, NULL }
+ { #name, test_bwmgt_ ## name , TT_FORK, NULL, NULL }
struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_init),
@@ -229,5 +440,7 @@ struct testcase_t bwmgt_tests[] = {
BWMGT(token_buf_dec),
BWMGT(token_buf_refill),
BWMGT(token_buf_helpers),
+
+ BWMGT(dir_conn_global_write_low),
END_OF_TESTCASES
};
diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c
index 6293839b0d..cf310ebfe0 100644
--- a/src/test/test_dir_handle_get.c
+++ b/src/test/test_dir_handle_get.c
@@ -2084,8 +2084,8 @@ test_dir_handle_get_status_vote_d(void* data)
const char *msg_out = NULL;
int status_out = 0;
- struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
- &status_out);
+ struct pending_vote_t *pv = dirvote_add_vote(VOTE_BODY_V3, 0,
+ &msg_out, &status_out);
tt_assert(pv);
status_vote_current_d_test(&header, &body, &body_used);
@@ -2459,8 +2459,8 @@ test_dir_handle_get_status_vote_next_authority(void* data)
time_t now = 1441223455 -1;
voting_schedule_recalculate_timing(mock_options, now);
- struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
- &status_out);
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, 0,
+ &msg_out, &status_out);
tt_assert(vote);
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
@@ -2619,8 +2619,8 @@ test_dir_handle_get_status_vote_current_authority(void* data)
time_t now = 1441223455;
voting_schedule_recalculate_timing(mock_options, now-1);
- struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, &msg_out,
- &status_out);
+ struct pending_vote_t *vote = dirvote_add_vote(VOTE_BODY_V3, 0,
+ &msg_out, &status_out);
tt_assert(vote);
// move the pending vote to previous vote
@@ -2658,6 +2658,183 @@ test_dir_handle_get_status_vote_current_authority(void* data)
dirvote_free_all();
}
+/* Test that a late vote is rejected, but an on-time vote is accepted. */
+static void
+test_dir_handle_get_status_vote_too_late(void* data)
+{
+ dir_connection_t *conn = NULL;
+ char *header = NULL, *body = NULL;
+ const char *msg_out = NULL;
+ int status_out = 0;
+ size_t body_used = 0;
+ const char digest[DIGEST_LEN] = "";
+
+ dir_server_t *ds = NULL;
+ const char* mode = (const char *)data;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+
+ mock_cert = authority_cert_parse_from_string(TEST_CERTIFICATE,
+ strlen(TEST_CERTIFICATE),
+ NULL);
+
+ /* create a trusted ds */
+ ds = trusted_dir_server_new("ds", "127.0.0.1", 9059, 9060, NULL, digest,
+ NULL, V3_DIRINFO, 1.0);
+ tt_assert(ds);
+ dir_server_add(ds);
+
+ /* ds v3_identity_digest is the certificate's identity_key */
+ base16_decode(ds->v3_identity_digest, DIGEST_LEN,
+ TEST_CERT_IDENT_KEY, HEX_DIGEST_LEN);
+
+ tt_int_op(0, OP_EQ, trusted_dirs_load_certs_from_string(TEST_CERTIFICATE,
+ TRUSTED_DIRS_CERTS_SRC_DL_BY_ID_DIGEST, 1, NULL));
+
+ init_mock_options();
+ mock_options->AuthoritativeDir = 1;
+ mock_options->V3AuthoritativeDir = 1;
+
+ int base_delay = 0;
+ int vote_interval = 0;
+ int start_offset = 0;
+
+ tt_assert(mode);
+ /* Set the required timings, see below for details */
+ if (strcmp(mode, "min") == 0) {
+ /* The minimum valid test network timing */
+ base_delay = 2;
+ vote_interval = 10;
+ start_offset = vote_interval - 5;
+ } else if (strcmp(mode, "chutney") == 0) {
+ /* The test network timing used by chutney */
+ base_delay = 4;
+ vote_interval = 20;
+ start_offset = vote_interval - 5;
+ } else if (strcmp(mode, "half-public") == 0) {
+ /* The short consensus failure timing used in the public network */
+ base_delay = 5*60;
+ vote_interval = 30*60;
+ start_offset = vote_interval - 9*60 - 5;
+ } else if (strcmp(mode, "public") == 0) {
+ /* The standard timing used in the public network */
+ base_delay = 5*60;
+ vote_interval = 60*60;
+ start_offset = vote_interval - 9*60 - 5;
+ }
+
+ tt_assert(base_delay > 0);
+ tt_assert(vote_interval > 0);
+ tt_assert(start_offset > 0);
+
+ /* Skew the time to fit the fixed time in the vote */
+ mock_options->TestingV3AuthVotingStartOffset = start_offset;
+ /* Calculate the rest of the timings */
+ mock_options->TestingV3AuthInitialVotingInterval = vote_interval;
+ mock_options->TestingV3AuthInitialVoteDelay = base_delay;
+ mock_options->TestingV3AuthInitialDistDelay = base_delay;
+
+ time_t now = 1441223455;
+ voting_schedule_recalculate_timing(mock_options, now-1);
+ const time_t voting_starts = voting_schedule.voting_starts;
+ const time_t fetch_missing = voting_schedule.fetch_missing_votes;
+
+ struct pending_vote_t *vote = NULL;
+
+ /* Next voting interval */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ fetch_missing + vote_interval,
+ &msg_out, &status_out);
+ tt_assert(!vote);
+ tt_int_op(status_out, OP_EQ, 400);
+ tt_str_op(msg_out, OP_EQ,
+ "Posted vote received too late, would be dangerous to count it");
+
+ /* Just after fetch missing */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ fetch_missing + 1,
+ &msg_out, &status_out);
+ tt_assert(!vote);
+ tt_int_op(status_out, OP_EQ, 400);
+ tt_str_op(msg_out, OP_EQ,
+ "Posted vote received too late, would be dangerous to count it");
+
+ /* On fetch missing */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ fetch_missing,
+ &msg_out, &status_out);
+ tt_assert(vote);
+
+ /* Move the pending vote to previous vote */
+ dirvote_act(mock_options, now+1);
+ /* And reset the timing */
+ voting_schedule_recalculate_timing(mock_options, now-1);
+
+ /* Between voting starts and fetch missing */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ voting_starts + 1,
+ &msg_out, &status_out);
+ tt_assert(vote);
+
+ /* Move the pending vote to previous vote */
+ dirvote_act(mock_options, now+1);
+ /* And reset the timing */
+ voting_schedule_recalculate_timing(mock_options, now-1);
+
+ /* On voting starts */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ voting_starts,
+ &msg_out, &status_out);
+ tt_assert(vote);
+
+ /* Move the pending vote to previous vote */
+ dirvote_act(mock_options, now+1);
+ /* And reset the timing */
+ voting_schedule_recalculate_timing(mock_options, now-1);
+
+ /* Just before voting starts */
+ vote = dirvote_add_vote(VOTE_BODY_V3,
+ voting_starts - 1,
+ &msg_out, &status_out);
+ tt_assert(vote);
+
+ /* Move the pending vote to previous vote */
+ dirvote_act(mock_options, now+1);
+
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
+
+ conn = new_dir_conn();
+ tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
+ GET("/tor/status-vote/current/authority"), NULL, 0));
+
+ fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
+ &body, &body_used, strlen(VOTE_BODY_V3)+1, 0);
+
+ tt_assert(header);
+ tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header);
+ tt_assert(strstr(header, "Content-Type: text/plain\r\n"));
+ tt_assert(strstr(header, "Content-Encoding: identity\r\n"));
+ tt_assert(strstr(header, "Content-Length: 4135\r\n"));
+
+ tt_str_op(VOTE_BODY_V3, OP_EQ, body);
+
+ done:
+ UNMOCK(connection_write_to_buf_impl_);
+ UNMOCK(get_my_v3_authority_cert);
+ connection_free_minimal(TO_CONN(conn));
+ tor_free(header);
+ tor_free(body);
+ authority_cert_free(mock_cert); mock_cert = NULL;
+ or_options_free(mock_options); mock_options = NULL;
+
+ clear_dir_servers();
+ routerlist_free_all();
+ dirvote_free_all();
+}
+
static void
test_dir_handle_get_parse_accept_encoding(void *arg)
{
@@ -2708,6 +2885,16 @@ test_dir_handle_get_parse_accept_encoding(void *arg)
#define DIR_HANDLE_CMD(name,flags) \
{ #name, test_dir_handle_get_##name, (flags), NULL, NULL }
+#ifdef COCCI
+/* Coccinelle doesn't like the stringification in this macro */
+#define DIR_HANDLE_CMD_ARG(name,flags,arg) \
+ DIR_HANDLE_CMD(name,flags)
+#else
+#define DIR_HANDLE_CMD_ARG(name,flags,arg) \
+ { #name "/" arg, test_dir_handle_get_##name, (flags), \
+ &passthrough_setup, (void *)(arg) }
+#endif /* defined(COCCI) */
+
struct testcase_t dir_handle_get_tests[] = {
DIR_HANDLE_CMD(not_found, 0),
DIR_HANDLE_CMD(bad_request, 0),
@@ -2747,6 +2934,10 @@ struct testcase_t dir_handle_get_tests[] = {
DIR_HANDLE_CMD(status_vote_next_not_found, 0),
DIR_HANDLE_CMD(status_vote_current_authority_not_found, 0),
DIR_HANDLE_CMD(status_vote_current_authority, 0),
+ DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "min"),
+ DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "chutney"),
+ DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "half-public"),
+ DIR_HANDLE_CMD_ARG(status_vote_too_late, 0, "public"),
DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0),
DIR_HANDLE_CMD(status_vote_next_authority, 0),
DIR_HANDLE_CMD(status_vote_next_bandwidth_not_found, 0),
diff --git a/src/test/test_dns.c b/src/test/test_dns.c
index ec17e9e91e..299321ab64 100644
--- a/src/test/test_dns.c
+++ b/src/test/test_dns.c
@@ -80,11 +80,11 @@ test_dns_clip_ttl(void *arg)
{
(void)arg;
- uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2;
+ uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2;
- tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),OP_EQ,MIN_DNS_TTL_AT_EXIT);
- tt_int_op(dns_clip_ttl(ttl_mid),OP_EQ,MAX_DNS_TTL_AT_EXIT);
- tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),OP_EQ,MAX_DNS_TTL_AT_EXIT);
+ tt_int_op(clip_dns_ttl(MIN_DNS_TTL - 1),OP_EQ,MIN_DNS_TTL);
+ tt_int_op(clip_dns_ttl(ttl_mid),OP_EQ,MAX_DNS_TTL);
+ tt_int_op(clip_dns_ttl(MAX_DNS_TTL + 1),OP_EQ,MAX_DNS_TTL);
done:
return;
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 8ea550b65f..f25bba3584 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -370,7 +370,7 @@ test_hsdir_revision_counter_check(void *arg)
hs_descriptor_t *published_desc = NULL;
char *published_desc_str = NULL;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
char *received_desc_str = NULL;
hs_descriptor_t *received_desc = NULL;
@@ -407,11 +407,11 @@ test_hsdir_revision_counter_check(void *arg)
const ed25519_public_key_t *blinded_key;
blinded_key = &published_desc->plaintext_data.blinded_pubkey;
- hs_get_subcredential(&signing_kp.pubkey, blinded_key, subcredential);
+ hs_get_subcredential(&signing_kp.pubkey, blinded_key, &subcredential);
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
retval = hs_desc_decode_descriptor(received_desc_str,
- subcredential, NULL, &received_desc);
+ &subcredential, NULL, &received_desc);
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(received_desc);
@@ -444,7 +444,7 @@ test_hsdir_revision_counter_check(void *arg)
received_desc_str = helper_fetch_desc_from_hsdir(blinded_key);
retval = hs_desc_decode_descriptor(received_desc_str,
- subcredential, NULL, &received_desc);
+ &subcredential, NULL, &received_desc);
tt_int_op(retval, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(received_desc);
@@ -476,7 +476,7 @@ test_client_cache(void *arg)
ed25519_keypair_t signing_kp;
hs_descriptor_t *published_desc = NULL;
char *published_desc_str = NULL;
- uint8_t wanted_subcredential[DIGEST256_LEN];
+ hs_subcredential_t wanted_subcredential;
response_handler_args_t *args = NULL;
dir_connection_t *conn = NULL;
@@ -505,8 +505,10 @@ test_client_cache(void *arg)
retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
NULL, &published_desc_str);
tt_int_op(retval, OP_EQ, 0);
- memcpy(wanted_subcredential, published_desc->subcredential, DIGEST256_LEN);
- tt_assert(!fast_mem_is_zero((char*)wanted_subcredential, DIGEST256_LEN));
+ memcpy(&wanted_subcredential, &published_desc->subcredential,
+ sizeof(hs_subcredential_t));
+ tt_assert(!fast_mem_is_zero((char*)wanted_subcredential.subcred,
+ DIGEST256_LEN));
}
/* Test handle_response_fetch_hsdesc_v3() */
@@ -540,8 +542,9 @@ test_client_cache(void *arg)
const hs_descriptor_t *cached_desc = NULL;
cached_desc = hs_cache_lookup_as_client(&signing_kp.pubkey);
tt_assert(cached_desc);
- tt_mem_op(cached_desc->subcredential, OP_EQ, wanted_subcredential,
- DIGEST256_LEN);
+ tt_mem_op(cached_desc->subcredential.subcred,
+ OP_EQ, wanted_subcredential.subcred,
+ SUBCRED_LEN);
}
/* Progress time to next TP and check that desc was cleaned */
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index 5f7fe9c404..2e603ec259 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -433,9 +433,10 @@ test_client_pick_intro(void *arg)
const hs_descriptor_t *fetched_desc =
hs_cache_lookup_as_client(&service_kp.pubkey);
tt_assert(fetched_desc);
- tt_mem_op(fetched_desc->subcredential, OP_EQ, desc->subcredential,
- DIGEST256_LEN);
- tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential,
+ tt_mem_op(fetched_desc->subcredential.subcred,
+ OP_EQ, desc->subcredential.subcred,
+ SUBCRED_LEN);
+ tt_assert(!fast_mem_is_zero((char*)fetched_desc->subcredential.subcred,
DIGEST256_LEN));
tor_free(encoded);
}
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index 61306778d4..20f88b637b 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -53,14 +53,14 @@ test_validate_address(void *arg)
setup_full_capture_of_logs(LOG_WARN);
ret = hs_address_is_valid("blah");
tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("has an invalid length");
+ expect_log_msg_containing("Invalid length");
teardown_capture_of_logs();
setup_full_capture_of_logs(LOG_WARN);
ret = hs_address_is_valid(
"p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb");
tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("has an invalid length");
+ expect_log_msg_containing("Invalid length");
teardown_capture_of_logs();
/* Invalid checksum (taken from prop224) */
@@ -83,7 +83,7 @@ test_validate_address(void *arg)
ret = hs_address_is_valid(
"????????????????????????????????????????????????????????");
tt_int_op(ret, OP_EQ, 0);
- expect_log_msg_containing("can't be decoded");
+ expect_log_msg_containing("Unable to base32 decode");
teardown_capture_of_logs();
/* Valid address. */
diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c
index 43ac5490a1..61ccd3f919 100644
--- a/src/test/test_hs_descriptor.c
+++ b/src/test/test_hs_descriptor.c
@@ -221,7 +221,7 @@ test_decode_descriptor(void *arg)
hs_descriptor_t *desc = NULL;
hs_descriptor_t *decoded = NULL;
hs_descriptor_t *desc_no_ip = NULL;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
(void) arg;
@@ -230,10 +230,10 @@ test_decode_descriptor(void *arg)
desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
- subcredential);
+ &subcredential);
/* Give some bad stuff to the decoding function. */
- ret = hs_desc_decode_descriptor("hladfjlkjadf", subcredential,
+ ret = hs_desc_decode_descriptor("hladfjlkjadf", &subcredential,
NULL, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_PLAINTEXT_ERROR);
@@ -241,7 +241,7 @@ test_decode_descriptor(void *arg)
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(encoded);
- ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
+ ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(decoded);
@@ -253,7 +253,7 @@ test_decode_descriptor(void *arg)
ret = ed25519_keypair_generate(&signing_kp_no_ip, 0);
tt_int_op(ret, OP_EQ, 0);
hs_helper_get_subcred_from_identity_keypair(&signing_kp_no_ip,
- subcredential);
+ &subcredential);
desc_no_ip = hs_helper_build_hs_desc_no_ip(&signing_kp_no_ip);
tt_assert(desc_no_ip);
tor_free(encoded);
@@ -262,7 +262,7 @@ test_decode_descriptor(void *arg)
tt_int_op(ret, OP_EQ, 0);
tt_assert(encoded);
hs_descriptor_free(decoded);
- ret = hs_desc_decode_descriptor(encoded, subcredential, NULL, &decoded);
+ ret = hs_desc_decode_descriptor(encoded, &subcredential, NULL, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(decoded);
}
@@ -286,14 +286,14 @@ test_decode_descriptor(void *arg)
&auth_ephemeral_kp.pubkey, CURVE25519_PUBKEY_LEN);
hs_helper_get_subcred_from_identity_keypair(&signing_kp,
- subcredential);
+ &subcredential);
/* Build and add the auth client to the descriptor. */
clients = desc->superencrypted_data.clients;
if (!clients) {
clients = smartlist_new();
}
- hs_desc_build_authorized_client(subcredential,
+ hs_desc_build_authorized_client(&subcredential,
&client_kp.pubkey,
&auth_ephemeral_kp.seckey,
descriptor_cookie, client);
@@ -315,21 +315,21 @@ test_decode_descriptor(void *arg)
/* If we do not have the client secret key, the decoding must fail. */
hs_descriptor_free(decoded);
- ret = hs_desc_decode_descriptor(encoded, subcredential,
+ ret = hs_desc_decode_descriptor(encoded, &subcredential,
NULL, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_NEED_CLIENT_AUTH);
tt_assert(!decoded);
/* If we have an invalid client secret key, the decoding must fail. */
hs_descriptor_free(decoded);
- ret = hs_desc_decode_descriptor(encoded, subcredential,
+ ret = hs_desc_decode_descriptor(encoded, &subcredential,
&invalid_client_kp.seckey, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_BAD_CLIENT_AUTH);
tt_assert(!decoded);
/* If we have the client secret key, the decoding must succeed and the
* decoded descriptor must be correct. */
- ret = hs_desc_decode_descriptor(encoded, subcredential,
+ ret = hs_desc_decode_descriptor(encoded, &subcredential,
&client_kp.seckey, &decoded);
tt_int_op(ret, OP_EQ, HS_DESC_DECODE_OK);
tt_assert(decoded);
@@ -762,7 +762,7 @@ test_build_authorized_client(void *arg)
"07d087f1d8c68393721f6e70316d3b29";
const char client_pubkey_b16[] =
"8c1298fa6050e372f8598f6deca32e27b0ad457741422c2629ebb132cf7fae37";
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
char *mem_op_hex_tmp=NULL;
(void) arg;
@@ -774,7 +774,7 @@ test_build_authorized_client(void *arg)
tt_int_op(ret, OP_EQ, 0);
curve25519_public_key_generate(&client_auth_pk, &client_auth_sk);
- memset(subcredential, 42, sizeof(subcredential));
+ memset(subcredential.subcred, 42, sizeof(subcredential));
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));
@@ -795,7 +795,7 @@ test_build_authorized_client(void *arg)
testing_enable_prefilled_rng("\x01", 1);
- hs_desc_build_authorized_client(subcredential,
+ hs_desc_build_authorized_client(&subcredential,
&client_auth_pk, &auth_ephemeral_sk,
descriptor_cookie, desc_client);
diff --git a/src/test/test_hs_ntor.c b/src/test/test_hs_ntor.c
index 4f98bc85dc..7867740a1a 100644
--- a/src/test/test_hs_ntor.c
+++ b/src/test/test_hs_ntor.c
@@ -23,7 +23,7 @@ test_hs_ntor(void *arg)
{
int retval;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
ed25519_keypair_t service_intro_auth_keypair;
curve25519_keypair_t service_intro_enc_keypair;
@@ -42,7 +42,7 @@ test_hs_ntor(void *arg)
/* Generate fake data for this unittest */
{
/* Generate fake subcredential */
- memset(subcredential, 'Z', DIGEST256_LEN);
+ memset(subcredential.subcred, 'Z', DIGEST256_LEN);
/* service */
curve25519_keypair_generate(&service_intro_enc_keypair, 0);
@@ -57,7 +57,7 @@ test_hs_ntor(void *arg)
hs_ntor_client_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
&service_intro_enc_keypair.pubkey,
&client_ephemeral_enc_keypair,
- subcredential,
+ &subcredential,
&client_hs_ntor_intro_cell_keys);
tt_int_op(retval, OP_EQ, 0);
@@ -66,7 +66,7 @@ test_hs_ntor(void *arg)
hs_ntor_service_get_introduce1_keys(&service_intro_auth_keypair.pubkey,
&service_intro_enc_keypair,
&client_ephemeral_enc_keypair.pubkey,
- subcredential,
+ &subcredential,
&service_hs_ntor_intro_cell_keys);
tt_int_op(retval, OP_EQ, 0);
diff --git a/src/test/test_hs_ntor_cl.c b/src/test/test_hs_ntor_cl.c
index a7cebc6af4..3acd7ef0bc 100644
--- a/src/test/test_hs_ntor_cl.c
+++ b/src/test/test_hs_ntor_cl.c
@@ -53,7 +53,7 @@ client1(int argc, char **argv)
curve25519_public_key_t intro_enc_pubkey;
ed25519_public_key_t intro_auth_pubkey;
curve25519_keypair_t client_ephemeral_enc_keypair;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
/* Output */
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
@@ -65,7 +65,7 @@ client1(int argc, char **argv)
BASE16(3, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
BASE16(4, client_ephemeral_enc_keypair.seckey.secret_key,
CURVE25519_SECKEY_LEN);
- BASE16(5, subcredential, DIGEST256_LEN);
+ BASE16(5, subcredential.subcred, DIGEST256_LEN);
/* Generate keypair */
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
@@ -74,7 +74,7 @@ client1(int argc, char **argv)
retval = hs_ntor_client_get_introduce1_keys(&intro_auth_pubkey,
&intro_enc_pubkey,
&client_ephemeral_enc_keypair,
- subcredential,
+ &subcredential,
&hs_ntor_intro_cell_keys);
if (retval < 0) {
goto done;
@@ -106,7 +106,7 @@ server1(int argc, char **argv)
curve25519_keypair_t intro_enc_keypair;
ed25519_public_key_t intro_auth_pubkey;
curve25519_public_key_t client_ephemeral_enc_pubkey;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
/* Output */
hs_ntor_intro_cell_keys_t hs_ntor_intro_cell_keys;
@@ -119,7 +119,7 @@ server1(int argc, char **argv)
BASE16(2, intro_auth_pubkey.pubkey, ED25519_PUBKEY_LEN);
BASE16(3, intro_enc_keypair.seckey.secret_key, CURVE25519_SECKEY_LEN);
BASE16(4, client_ephemeral_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
- BASE16(5, subcredential, DIGEST256_LEN);
+ BASE16(5, subcredential.subcred, DIGEST256_LEN);
/* Generate keypair */
curve25519_public_key_generate(&intro_enc_keypair.pubkey,
@@ -130,7 +130,7 @@ server1(int argc, char **argv)
retval = hs_ntor_service_get_introduce1_keys(&intro_auth_pubkey,
&intro_enc_keypair,
&client_ephemeral_enc_pubkey,
- subcredential,
+ &subcredential,
&hs_ntor_intro_cell_keys);
if (retval < 0) {
goto done;
@@ -188,7 +188,7 @@ client2(int argc, char **argv)
ed25519_public_key_t intro_auth_pubkey;
curve25519_keypair_t client_ephemeral_enc_keypair;
curve25519_public_key_t service_ephemeral_rend_pubkey;
- uint8_t subcredential[DIGEST256_LEN];
+ hs_subcredential_t subcredential;
/* Output */
hs_ntor_rend_cell_keys_t hs_ntor_rend_cell_keys;
@@ -201,7 +201,7 @@ client2(int argc, char **argv)
CURVE25519_SECKEY_LEN);
BASE16(4, intro_enc_pubkey.public_key, CURVE25519_PUBKEY_LEN);
BASE16(5, service_ephemeral_rend_pubkey.public_key, CURVE25519_PUBKEY_LEN);
- BASE16(6, subcredential, DIGEST256_LEN);
+ BASE16(6, subcredential.subcred, DIGEST256_LEN);
/* Generate keypair */
curve25519_public_key_generate(&client_ephemeral_enc_keypair.pubkey,
diff --git a/src/test/test_hs_ob.c b/src/test/test_hs_ob.c
new file mode 100644
index 0000000000..7f40187b5f
--- /dev/null
+++ b/src/test/test_hs_ob.c
@@ -0,0 +1,268 @@
+/* Copyright (c) 2020, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file test_hs_ob.c
+ * \brief Test hidden service onion balance functionality.
+ */
+
+#define CONFIG_PRIVATE
+#define HS_SERVICE_PRIVATE
+#define HS_OB_PRIVATE
+
+#include "test/test.h"
+#include "test/test_helpers.h"
+#include "test/log_test_helpers.h"
+
+#include "app/config/config.h"
+#include "feature/hs/hs_config.h"
+#include "feature/hs/hs_ob.h"
+#include "feature/hs/hs_service.h"
+#include "feature/nodelist/networkstatus.h"
+#include "feature/nodelist/networkstatus_st.h"
+
+static ed25519_keypair_t onion_addr_kp_1;
+static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+static ed25519_keypair_t onion_addr_kp_2;
+static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+
+static bool config_is_good = true;
+
+static int
+helper_tor_config(const char *conf)
+{
+ int ret = -1;
+ or_options_t *options = helper_parse_options(conf);
+ tt_assert(options);
+ ret = hs_config_service_all(options, 0);
+ done:
+ or_options_free(options);
+ return ret;
+}
+
+static networkstatus_t mock_ns;
+
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return &mock_ns;
+}
+
+static char *
+mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
+{
+ char *ret = NULL;
+
+ (void) flags;
+ (void) stat_out;
+
+ if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR "ob_config"))) {
+ if (config_is_good) {
+ tor_asprintf(&ret, "MasterOnionAddress %s.onion\n"
+ "MasterOnionAddress %s.onion\n",
+ onion_addr_1, onion_addr_2);
+ } else {
+ tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n"
+ "UnknownOption BLAH\n");
+ }
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+static void
+test_parse_config_file(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+ const ed25519_public_key_t *pkey;
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(read_file_to_str, mock_read_file_to_str);
+
+#define fmt_conf \
+ "HiddenServiceDir %s\n" \
+ "HiddenServicePort 22\n" \
+ "HiddenServiceOnionBalanceInstance 1\n"
+ tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
+#undef fmt_conf
+
+ /* Build the OB frontend onion addresses. */
+ ed25519_keypair_generate(&onion_addr_kp_1, 0);
+ hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1);
+ ed25519_keypair_generate(&onion_addr_kp_2, 0);
+ hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2);
+
+ ret = helper_tor_config(conf);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, 0);
+
+ /* Load the keys for the service. After that, the v3 service should be
+ * registered in the global map and we'll be able to access it. */
+ tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
+ hs_service_load_all_keys();
+ tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
+ const hs_service_t *s = get_first_service();
+ tt_assert(s);
+ tt_assert(s->config.ob_master_pubkeys);
+ tt_assert(hs_ob_service_is_instance(s));
+ tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2);
+
+ /* Test the public keys we've added. */
+ pkey = smartlist_get(s->config.ob_master_pubkeys, 0);
+ tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
+ pkey = smartlist_get(s->config.ob_master_pubkeys, 1);
+ tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
+
+ done:
+ hs_free_all();
+
+ UNMOCK(read_file_to_str);
+}
+
+static void
+test_parse_config_file_bad(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(read_file_to_str, mock_read_file_to_str);
+
+ /* Indicate mock_read_file_to_str() to use the bad config. */
+ config_is_good = false;
+
+#define fmt_conf \
+ "HiddenServiceDir %s\n" \
+ "HiddenServicePort 22\n" \
+ "HiddenServiceOnionBalanceInstance 1\n"
+ tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
+#undef fmt_conf
+
+ setup_full_capture_of_logs(LOG_INFO);
+ ret = helper_tor_config(conf);
+ tor_free(conf);
+ tt_int_op(ret, OP_EQ, -1);
+ expect_log_msg_containing("OnionBalance: MasterOnionAddress "
+ "JUNKJUNKJUNK.onion is invalid");
+ expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; "
+ "saving it.");
+ teardown_capture_of_logs();
+
+ done:
+ hs_free_all();
+
+ UNMOCK(read_file_to_str);
+}
+
+static void
+test_get_subcredentials(void *arg)
+{
+ int ret;
+ hs_service_t *service = NULL;
+ hs_service_config_t config;
+ hs_subcredential_t *subcreds = NULL;
+
+ (void) arg;
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ /* Setup consensus with proper time so we can compute the time period. */
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+
+ config.ob_master_pubkeys = smartlist_new();
+ tt_assert(config.ob_master_pubkeys);
+
+ /* Set up an instance */
+ service = tor_malloc_zero(sizeof(hs_service_t));
+ service->config = config;
+ /* Setup the service descriptors */
+ service->desc_current = service_descriptor_new();
+ service->desc_next = service_descriptor_new();
+
+ /* First try to compute subcredentials but with no OB keys. Make sure that
+ * subcreds get NULLed. To do this check we first poison subcreds. */
+ subcreds = (void*)999;
+ tt_ptr_op(subcreds, OP_NE, NULL);
+ size_t num = compute_subcredentials(service, &subcreds);
+ tt_ptr_op(subcreds, OP_EQ, NULL);
+
+ /* Generate a keypair to add to the OB keys list. */
+ ed25519_keypair_generate(&onion_addr_kp_1, 0);
+ smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey);
+
+ /* Set up the instance subcredentials */
+ char current_subcred[SUBCRED_LEN];
+ char next_subcred[SUBCRED_LEN];
+ memset(current_subcred, 'C', SUBCRED_LEN);
+ memset(next_subcred, 'N', SUBCRED_LEN);
+ memcpy(service->desc_current->desc->subcredential.subcred, current_subcred,
+ SUBCRED_LEN);
+ memcpy(service->desc_next->desc->subcredential.subcred, next_subcred,
+ SUBCRED_LEN);
+
+ /* See that subcreds are computed properly */
+ num = compute_subcredentials(service, &subcreds);
+ /* 5 subcredentials: 3 for the frontend, 2 for the instance */
+ tt_uint_op(num, OP_EQ, 5);
+ tt_ptr_op(subcreds, OP_NE, NULL);
+
+ /* Validate the subcredentials we just got. We'll build them oursevles with
+ * the right time period steps and compare. */
+ const uint64_t tp = hs_get_time_period_num(0);
+ const int steps[3] = {0, -1, 1};
+
+ unsigned int i;
+ for (i = 0; i < 3; i++) {
+ hs_subcredential_t subcredential;
+ ed25519_public_key_t blinded_pubkey;
+ hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i],
+ &blinded_pubkey);
+ hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey,
+ &subcredential);
+ tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred,
+ SUBCRED_LEN);
+ }
+
+ tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN);
+ tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN);
+
+ done:
+ tor_free(subcreds);
+
+ smartlist_free(config.ob_master_pubkeys);
+ if (service) {
+ memset(&service->config, 0, sizeof(hs_service_config_t));
+ hs_service_free(service);
+ }
+
+ UNMOCK(networkstatus_get_live_consensus);
+}
+
+struct testcase_t hs_ob_tests[] = {
+ { "parse_config_file", test_parse_config_file, TT_FORK,
+ NULL, NULL },
+ { "parse_config_file_bad", test_parse_config_file_bad, TT_FORK,
+ NULL, NULL },
+
+ { "get_subcredentials", test_get_subcredentials, TT_FORK,
+ NULL, NULL },
+
+ END_OF_TESTCASES
+};
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index e33d593d94..1767648bbe 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -51,6 +51,8 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_ident.h"
+#include "feature/hs/hs_ob.h"
+#include "feature/hs/hs_cell.h"
#include "feature/hs/hs_intropoint.h"
#include "feature/hs/hs_service.h"
#include "feature/nodelist/networkstatus.h"
@@ -109,6 +111,9 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line,
return;
}
+static size_t relay_payload_len;
+static char relay_payload[RELAY_PAYLOAD_SIZE];
+
static int
mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
uint8_t relay_command, const char *payload,
@@ -124,6 +129,10 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ,
(void) cpath_layer;
(void) filename;
(void) lineno;
+
+ memcpy(relay_payload, payload, payload_len);
+ relay_payload_len = payload_len;
+
return 0;
}
@@ -1160,7 +1169,7 @@ test_closing_intro_circs(void *arg)
/** Test sending and receiving introduce2 cells */
static void
-test_introduce2(void *arg)
+test_bad_introduce2(void *arg)
{
int ret;
int flags = CIRCLAUNCH_NEED_UPTIME | CIRCLAUNCH_IS_INTERNAL;
@@ -2169,6 +2178,348 @@ test_export_client_circuit_id(void *arg)
tor_free(cp2);
}
+static smartlist_t *
+mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn)
+{
+ (void) node;
+ (void) direct_conn;
+
+ smartlist_t *lspecs = smartlist_new();
+ link_specifier_t *ls_legacy = link_specifier_new();
+ smartlist_add(lspecs, ls_legacy);
+
+ return lspecs;
+}
+
+static node_t *fake_node = NULL;
+
+static const node_t *
+mock_build_state_get_exit_node(cpath_build_state_t *state)
+{
+ (void) state;
+
+ if (!fake_node) {
+ curve25519_secret_key_t seckey;
+ curve25519_secret_key_generate(&seckey, 0);
+
+ fake_node = tor_malloc_zero(sizeof(node_t));
+ fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t));
+ fake_node->ri->onion_curve25519_pkey =
+ tor_malloc_zero(sizeof(curve25519_public_key_t));
+ curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey,
+ &seckey);
+ }
+
+ return fake_node;
+}
+
+static void
+mock_launch_rendezvous_point_circuit(const hs_service_t *service,
+ const hs_service_intro_point_t *ip,
+ const hs_cell_introduce2_data_t *data)
+{
+ (void) service;
+ (void) ip;
+ (void) data;
+ return;
+}
+
+/**
+ * Test that INTRO2 cells are handled well by onion services in the normal
+ * case and also when onionbalance is enabled.
+ */
+static void
+test_intro2_handling(void *arg)
+{
+ (void)arg;
+
+ MOCK(build_state_get_exit_node, mock_build_state_get_exit_node);
+ MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge);
+ MOCK(node_get_link_specifier_smartlist,
+ mock_node_get_link_specifier_smartlist);
+ MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit);
+
+ memset(relay_payload, 0, sizeof(relay_payload));
+
+ int retval;
+ time_t now = 0101010101;
+ update_approx_time(now);
+
+ /** OK this is the play:
+ *
+ * In Act I, we have a standalone onion service X (without onionbalance
+ * enabled). We test that X can properly handle INTRO2 cells sent by a
+ * client Alice.
+ *
+ * In Act II, we create an onionbalance setup with frontend being Z which
+ * includes instances X and Y. We then setup onionbalance on X and test that
+ * Alice who addresses Z can communicate with X through INTRO2 cells.
+ *
+ * In Act III, we test that Alice can also communicate with X
+ * directly even tho onionbalance is enabled.
+ *
+ * And finally in Act IV, we check various cases where the INTRO2 cell
+ * should not go through because the subcredentials don't line up
+ * (e.g. Alice sends INTRO2 to X using Y's subcredential).
+ */
+
+ /** Let's start with some setup! Create the instances and the frontend
+ service, create Alice, etc: */
+
+ /* Create instance X */
+ hs_service_t x_service;
+ memset(&x_service, 0, sizeof(hs_service_t));
+ /* Disable onionbalance */
+ x_service.config.ob_master_pubkeys = NULL;
+ x_service.state.replay_cache_rend_cookie = replaycache_new(0,0);
+
+ /* Create subcredential for x: */
+ ed25519_keypair_t x_identity_keypair;
+ hs_subcredential_t x_subcred;
+ ed25519_keypair_generate(&x_identity_keypair, 0);
+ hs_helper_get_subcred_from_identity_keypair(&x_identity_keypair,
+ &x_subcred);
+
+ /* Create the x instance's intro point */
+ hs_service_intro_point_t *x_ip = NULL;
+ {
+ curve25519_secret_key_t seckey;
+ curve25519_public_key_t pkey;
+ curve25519_secret_key_generate(&seckey, 0);
+ curve25519_public_key_generate(&pkey, &seckey);
+
+ node_t intro_node;
+ memset(&intro_node, 0, sizeof(intro_node));
+ routerinfo_t ri;
+ memset(&ri, 0, sizeof(routerinfo_t));
+ ri.onion_curve25519_pkey = &pkey;
+ intro_node.ri = &ri;
+
+ x_ip = service_intro_point_new(&intro_node);
+ }
+
+ /* Create z frontend's subcredential */
+ ed25519_keypair_t z_identity_keypair;
+ hs_subcredential_t z_subcred;
+ ed25519_keypair_generate(&z_identity_keypair, 0);
+ hs_helper_get_subcred_from_identity_keypair(&z_identity_keypair,
+ &z_subcred);
+
+ /* Create y instance's subcredential */
+ ed25519_keypair_t y_identity_keypair;
+ hs_subcredential_t y_subcred;
+ ed25519_keypair_generate(&y_identity_keypair, 0);
+ hs_helper_get_subcred_from_identity_keypair(&y_identity_keypair,
+ &y_subcred);
+
+ /* Create Alice's intro point */
+ hs_desc_intro_point_t *alice_ip;
+ ed25519_keypair_t signing_kp;
+ ed25519_keypair_generate(&signing_kp, 0);
+ alice_ip = hs_helper_build_intro_point(&signing_kp, now, "1.2.3.4", 0,
+ &x_ip->auth_key_kp,
+ &x_ip->enc_key_kp);
+
+ /* Create Alice's intro and rend circuits */
+ origin_circuit_t *intro_circ = origin_circuit_new();
+ intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t));
+ intro_circ->cpath->prev = intro_circ->cpath;
+ intro_circ->hs_ident = tor_malloc_zero(sizeof(*intro_circ->hs_ident));
+ origin_circuit_t rend_circ;
+ rend_circ.hs_ident = tor_malloc_zero(sizeof(*rend_circ.hs_ident));
+ curve25519_keypair_generate(&rend_circ.hs_ident->rendezvous_client_kp, 0);
+ memset(rend_circ.hs_ident->rendezvous_cookie, 'r', HS_REND_COOKIE_LEN);
+
+ /* ************************************************************ */
+
+ /* Act I:
+ *
+ * Where Alice connects to X without onionbalance in the picture */
+
+ /* Create INTRODUCE1 */
+ tt_assert(fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+ retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+ alice_ip, &x_subcred);
+
+ /* Check that the payload was written successfully */
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ /* Handle the cell */
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &x_subcred,
+ (uint8_t*)relay_payload,relay_payload_len);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* ************************************************************ */
+
+ /* Act II:
+ *
+ * We now create an onionbalance setup with Z being the frontend and X and Y
+ * being the backend instances. Make sure that Alice can talk with the
+ * backend instance X even tho she thinks she is talking to the frontend Z.
+ */
+
+ /* Now configure the X instance to do onionbalance with Z as the frontend */
+ x_service.config.ob_master_pubkeys = smartlist_new();
+ smartlist_add(x_service.config.ob_master_pubkeys,
+ &z_identity_keypair.pubkey);
+
+ /* Create descriptors for x and load next descriptor with the x's
+ * subcredential so that it can accept connections for itself. */
+ x_service.desc_current = service_descriptor_new();
+ memset(x_service.desc_current->desc->subcredential.subcred, 'C',SUBCRED_LEN);
+ x_service.desc_next = service_descriptor_new();
+ memcpy(&x_service.desc_next->desc->subcredential, &x_subcred, SUBCRED_LEN);
+
+ /* Refresh OB keys */
+ hs_ob_refresh_keys(&x_service);
+
+ /* Create INTRODUCE1 from Alice to X through Z */
+ memset(relay_payload, 0, sizeof(relay_payload));
+ retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+ alice_ip, &z_subcred);
+
+ /* Check that the payload was written successfully */
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ /* Deliver INTRODUCE1 to X even tho it carries Z's subcredential */
+ replaycache_free(x_service.state.replay_cache_rend_cookie);
+ x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &z_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, 0);
+
+ replaycache_free(x_ip->replay_cache);
+ x_ip->replay_cache = replaycache_new(0, 0);
+
+ replaycache_free(x_service.state.replay_cache_rend_cookie);
+ x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+ /* ************************************************************ */
+
+ /* Act III:
+ *
+ * Now send a direct INTRODUCE cell from Alice to X using X's subcredential
+ * and check that it succeeds even with onionbalance enabled.
+ */
+
+ /* Refresh OB keys (just to check for memleaks) */
+ hs_ob_refresh_keys(&x_service);
+
+ /* Create INTRODUCE1 from Alice to X using X's subcred. */
+ memset(relay_payload, 0, sizeof(relay_payload));
+ retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+ alice_ip, &x_subcred);
+
+ /* Check that the payload was written successfully */
+ tt_int_op(retval, OP_EQ, 0);
+ tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ /* Send INTRODUCE1 to X with X's subcredential (should succeed) */
+ replaycache_free(x_service.state.replay_cache_rend_cookie);
+ x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &x_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* ************************************************************ */
+
+ /* Act IV:
+ *
+ * Test cases where the INTRO2 cell should not be able to decode.
+ */
+
+ /* Try sending the exact same INTRODUCE2 cell again and see that the intro
+ * point replay cache triggers: */
+ setup_full_capture_of_logs(LOG_WARN);
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &x_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, -1);
+ expect_log_msg_containing("with the same ENCRYPTED section");
+ teardown_capture_of_logs();
+
+ /* Now cleanup the intro point replay cache but not the service replay cache
+ and see that this one triggers this time. */
+ replaycache_free(x_ip->replay_cache);
+ x_ip->replay_cache = replaycache_new(0, 0);
+ setup_full_capture_of_logs(LOG_INFO);
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &x_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, -1);
+ expect_log_msg_containing("with same REND_COOKIE");
+ teardown_capture_of_logs();
+
+ /* Now just to make sure cleanup both replay caches and make sure that the
+ cell gets through */
+ replaycache_free(x_ip->replay_cache);
+ x_ip->replay_cache = replaycache_new(0, 0);
+ replaycache_free(x_service.state.replay_cache_rend_cookie);
+ x_service.state.replay_cache_rend_cookie = replaycache_new(0, 0);
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &x_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* As a final thing, create an INTRODUCE1 cell from Alice to X using Y's
+ * subcred (should fail since Y is just another instance and not the frontend
+ * service!) */
+ memset(relay_payload, 0, sizeof(relay_payload));
+ retval = hs_circ_send_introduce1(intro_circ, &rend_circ,
+ alice_ip, &y_subcred);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check that the payload was written successfully */
+ tt_assert(!fast_mem_is_zero(relay_payload, sizeof(relay_payload)));
+ tt_int_op(relay_payload_len, OP_NE, 0);
+
+ retval = hs_circ_handle_introduce2(&x_service,
+ intro_circ, x_ip,
+ &y_subcred,
+ (uint8_t*)relay_payload, relay_payload_len);
+ tt_int_op(retval, OP_EQ, -1);
+
+ done:
+ /* Start cleaning up X */
+ replaycache_free(x_service.state.replay_cache_rend_cookie);
+ smartlist_free(x_service.config.ob_master_pubkeys);
+ tor_free(x_service.ob_subcreds);
+ service_descriptor_free(x_service.desc_current);
+ service_descriptor_free(x_service.desc_next);
+ service_intro_point_free(x_ip);
+
+ /* Clean up Alice */
+ hs_desc_intro_point_free(alice_ip);
+ tor_free(rend_circ.hs_ident);
+
+ if (fake_node) {
+ tor_free(fake_node->ri->onion_curve25519_pkey);
+ tor_free(fake_node->ri);
+ tor_free(fake_node);
+ }
+
+ UNMOCK(build_state_get_exit_node);
+ UNMOCK(relay_send_command_from_edge_);
+ UNMOCK(node_get_link_specifier_smartlist);
+ UNMOCK(launch_rendezvous_point_circuit);
+}
+
struct testcase_t hs_service_tests[] = {
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
NULL, NULL },
@@ -2194,7 +2545,7 @@ struct testcase_t hs_service_tests[] = {
NULL, NULL },
{ "rdv_circuit_opened", test_rdv_circuit_opened, TT_FORK,
NULL, NULL },
- { "introduce2", test_introduce2, TT_FORK,
+ { "bad_introduce2", test_bad_introduce2, TT_FORK,
NULL, NULL },
{ "service_event", test_service_event, TT_FORK,
NULL, NULL },
@@ -2212,6 +2563,7 @@ struct testcase_t hs_service_tests[] = {
TT_FORK, NULL, NULL },
{ "export_client_circuit_id", test_export_client_circuit_id, TT_FORK,
NULL, NULL },
+ { "intro2_handling", test_intro2_handling, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
index 96542ce7ac..1566b349ed 100644
--- a/src/test/test_link_handshake.c
+++ b/src/test/test_link_handshake.c
@@ -18,6 +18,7 @@
#include "feature/relay/routerkeys.h"
#include "core/or/scheduler.h"
#include "feature/nodelist/torcert.h"
+#include "feature/relay/relay_handshake.h"
#include "core/or/or_connection_st.h"
#include "core/or/or_handshake_certs_st.h"
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 0d86a5ab5d..b1e4335652 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4572,6 +4572,35 @@ test_util_di_ops(void *arg)
}
static void
+test_util_memcpy_iftrue_timei(void *arg)
+{
+ (void)arg;
+ char buf1[25];
+ char buf2[25];
+ char buf3[25];
+
+ for (int i = 0; i < 100; ++i) {
+ crypto_rand(buf1, sizeof(buf1));
+ crypto_rand(buf2, sizeof(buf2));
+ memcpy(buf3, buf1, sizeof(buf1));
+
+ /* We just copied buf1 into buf3. Now we're going to copy buf2 into buf2,
+ iff our coin flip comes up heads. */
+ bool coinflip = crypto_rand_int(2) == 0;
+
+ memcpy_if_true_timei(coinflip, buf3, buf2, sizeof(buf3));
+
+ if (coinflip) {
+ tt_mem_op(buf3, OP_EQ, buf2, sizeof(buf2));
+ } else {
+ tt_mem_op(buf3, OP_EQ, buf1, sizeof(buf1));
+ }
+ }
+ done:
+ ;
+}
+
+static void
test_util_di_map(void *arg)
{
(void)arg;
@@ -6386,6 +6415,7 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(path_is_relative),
UTIL_LEGACY(strtok),
UTIL_LEGACY(di_ops),
+ UTIL_TEST(memcpy_iftrue_timei, 0),
UTIL_TEST(di_map, 0),
UTIL_TEST(round_to_next_multiple_of, 0),
UTIL_TEST(laplace, 0),
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 655e36e336..16a62bd532 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -273,7 +273,7 @@ main(int c, const char **v)
int loglevel = LOG_ERR;
int accel_crypto = 0;
- subsystems_init_upto(SUBSYS_LEVEL_LIBS);
+ subsystems_init();
options = options_new();