commit d692c5fd0368da79f83307f6f023ae5793472e1a
Author: David Goulet <dgoulet(a)torproject.org>
Date: Mon Aug 12 11:59:51 2019 -0400
hs-v3: Encode DoS ESTABLISH_INTRO cell extension
This commit makes tor add the DoS cell extension to the ESTABLISH_INTRO cell
if the defense is enabled on the service side with a torrc option.
Furthermore, the cell extension is only added if the introduction point
supports it. The protover version HSIntro=5 is looked for.
…
[View More]
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/hs_cell.c | 111 +++++++++++++++++++++++++++-
src/feature/hs/hs_cell.h | 1 +
src/feature/hs/hs_circuit.c | 2 +-
src/feature/hs/hs_service.c | 4 +
src/feature/hs/hs_service.h | 4 +
src/test/test_hs_cell.c | 9 ++-
src/test/test_hs_intropoint.c | 10 ++-
src/trunnel/hs/cell_establish_intro.h | 2 +
src/trunnel/hs/cell_establish_intro.trunnel | 4 +
9 files changed, 138 insertions(+), 9 deletions(-)
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 69f1ccbef..a67af1e9a 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -473,10 +473,110 @@ introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
}
}
+/* Build and add to the given DoS cell extension the given parameter type and
+ * value. */
+static void
+build_establish_intro_dos_param(trn_cell_extension_dos_t *dos_ext,
+ uint8_t param_type, uint64_t param_value)
+{
+ trn_cell_extension_dos_param_t *dos_param =
+ trn_cell_extension_dos_param_new();
+
+ /* Extra safety. We should never send an unknown parameter type. */
+ tor_assert(param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC ||
+ param_type == TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC);
+
+ trn_cell_extension_dos_param_set_type(dos_param, param_type);
+ trn_cell_extension_dos_param_set_value(dos_param, param_value);
+ trn_cell_extension_dos_add_params(dos_ext, dos_param);
+
+ /* Not freeing the trunnel object because it is now owned by dos_ext. */
+}
+
+/* Build the DoS defense cell extension and put it in the given extensions
+ * object. This can't fail. */
+static void
+build_establish_intro_dos_extension(const hs_service_config_t *service_config,
+ trn_cell_extension_t *extensions)
+{
+ ssize_t ret, dos_ext_encoded_len;
+ uint8_t *field_array;
+ trn_cell_extension_field_t *field;
+ trn_cell_extension_dos_t *dos_ext;
+
+ tor_assert(service_config);
+ tor_assert(extensions);
+
+ /* We are creating a cell extension field of the type DoS. */
+ field = trn_cell_extension_field_new();
+ trn_cell_extension_field_set_field_type(field,
+ TRUNNEL_CELL_EXTENSION_TYPE_DOS);
+
+ /* Build DoS extension field. We will put in two parameters. */
+ dos_ext = trn_cell_extension_dos_new();
+ trn_cell_extension_dos_set_n_params(dos_ext, 2);
+
+ /* Build DoS parameter INTRO2 rate per second. */
+ build_establish_intro_dos_param(dos_ext,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC,
+ service_config->intro_dos_rate_per_sec);
+ /* Build DoS parameter INTRO2 burst per second. */
+ build_establish_intro_dos_param(dos_ext,
+ TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC,
+ service_config->intro_dos_burst_per_sec);
+
+ /* Set the field with the encoded DoS extension. */
+ dos_ext_encoded_len = trn_cell_extension_dos_encoded_len(dos_ext);
+ /* Set length field and the field array size length. */
+ trn_cell_extension_field_set_field_len(field, dos_ext_encoded_len);
+ trn_cell_extension_field_setlen_field(field, dos_ext_encoded_len);
+ /* Encode the DoS extension into the cell extension field. */
+ field_array = trn_cell_extension_field_getarray_field(field);
+ ret = trn_cell_extension_dos_encode(field_array,
+ trn_cell_extension_field_getlen_field(field), dos_ext);
+ tor_assert(ret == dos_ext_encoded_len);
+
+ /* Finally, encode field into the cell extension. */
+ trn_cell_extension_add_fields(extensions, field);
+
+ /* We've just add an extension field to the cell extensions so increment the
+ * total number. */
+ trn_cell_extension_set_num(extensions,
+ trn_cell_extension_get_num(extensions) + 1);
+
+ /* Cleanup. DoS extension has been encoded at this point. */
+ trn_cell_extension_dos_free(dos_ext);
+}
+
/* ========== */
/* Public API */
/* ========== */
+/* Allocate and build all the ESTABLISH_INTRO cell extension. The given
+ * extensions pointer is always set to a valid cell extension object. */
+static trn_cell_extension_t *
+build_establish_intro_extensions(const hs_service_config_t *service_config,
+ const hs_service_intro_point_t *ip)
+{
+ trn_cell_extension_t *extensions;
+
+ tor_assert(service_config);
+ tor_assert(ip);
+
+ extensions = trn_cell_extension_new();
+ trn_cell_extension_set_num(extensions, 0);
+
+ /* If the defense has been enabled service side (by the operator with a
+ * torrc option) and the intro point does support it. */
+ if (service_config->has_dos_defense_enabled &&
+ ip->support_intro2_dos_defense) {
+ /* This function takes care to increment the number of extensions. */
+ build_establish_intro_dos_extension(service_config, extensions);
+ }
+
+ return extensions;
+}
+
/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
* object. The encoded cell is put in cell_out that MUST at least be of the
* size of RELAY_PAYLOAD_SIZE. Return the encoded cell length on success else
@@ -484,15 +584,17 @@ introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
* legacy cell creation. */
ssize_t
hs_cell_build_establish_intro(const char *circ_nonce,
+ const hs_service_config_t *service_config,
const hs_service_intro_point_t *ip,
uint8_t *cell_out)
{
ssize_t cell_len = -1;
uint16_t sig_len = ED25519_SIG_LEN;
- trn_cell_extension_t *ext;
trn_cell_establish_intro_t *cell = NULL;
+ trn_cell_extension_t *extensions;
tor_assert(circ_nonce);
+ tor_assert(service_config);
tor_assert(ip);
/* Quickly handle the legacy IP. */
@@ -505,11 +607,12 @@ hs_cell_build_establish_intro(const char *circ_nonce,
goto done;
}
+ /* Build the extensions, if any. */
+ extensions = build_establish_intro_extensions(service_config, ip);
+
/* Set extension data. None used here. */
- ext = trn_cell_extension_new();
- trn_cell_extension_set_num(ext, 0);
cell = trn_cell_establish_intro_new();
- trn_cell_establish_intro_set_extensions(cell, ext);
+ trn_cell_establish_intro_set_extensions(cell, extensions);
/* Set signature size. Array is then allocated in the cell. We need to do
* this early so we can use trunnel API to get the signature length. */
trn_cell_establish_intro_set_sig_len(cell, sig_len);
diff --git a/src/feature/hs/hs_cell.h b/src/feature/hs/hs_cell.h
index 9569de535..5fb416c2f 100644
--- a/src/feature/hs/hs_cell.h
+++ b/src/feature/hs/hs_cell.h
@@ -79,6 +79,7 @@ typedef struct hs_cell_introduce2_data_t {
/* Build cell API. */
ssize_t hs_cell_build_establish_intro(const char *circ_nonce,
+ const hs_service_config_t *config,
const hs_service_intro_point_t *ip,
uint8_t *cell_out);
ssize_t hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c
index a6e86c5ab..66f81189b 100644
--- a/src/feature/hs/hs_circuit.c
+++ b/src/feature/hs/hs_circuit.c
@@ -319,7 +319,7 @@ send_establish_intro(const hs_service_t *service,
/* Encode establish intro cell. */
cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce,
- ip, payload);
+ &service->config, ip, payload);
if (cell_len < 0) {
log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s "
"on circuit %u. Closing circuit.",
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index bbfade4d1..ef9bce98e 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -492,6 +492,10 @@ service_intro_point_new(const node_t *node)
}
}
+ /* Flag if this intro point supports the INTRO2 dos defenses. */
+ ip->support_intro2_dos_defense =
+ node_supports_establish_intro_dos_extension(node);
+
/* Finally, copy onion key from the node. */
memcpy(&ip->onion_key, node_get_curve25519_onion_key(node),
sizeof(ip->onion_key));
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index 77f6527c2..c4bbb293b 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -76,6 +76,10 @@ typedef struct hs_service_intro_point_t {
* circuit associated with this intro point has received. This is used to
* prevent replay attacks. */
replaycache_t *replay_cache;
+
+ /* Support the INTRO2 DoS defense. If set, the DoS extension described by
+ * proposal 305 is sent. */
+ unsigned int support_intro2_dos_defense : 1;
} hs_service_intro_point_t;
/* Object handling introduction points of a service. */
diff --git a/src/test/test_hs_cell.c b/src/test/test_hs_cell.c
index cdcbe23e6..874c04b20 100644
--- a/src/test/test_hs_cell.c
+++ b/src/test/test_hs_cell.c
@@ -38,11 +38,13 @@ test_gen_establish_intro_cell(void *arg)
/* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we
attempt to parse it. */
{
+ hs_service_config_t config;
+ memset(&config, 0, sizeof(config));
/* We only need the auth key pair here. */
hs_service_intro_point_t *ip = service_intro_point_new(NULL);
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
- ret = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ ret = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
service_intro_point_free(ip);
tt_u64_op(ret, OP_GT, 0);
}
@@ -97,6 +99,9 @@ test_gen_establish_intro_cell_bad(void *arg)
trn_cell_establish_intro_t *cell = NULL;
char circ_nonce[DIGEST_LEN] = {0};
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed);
@@ -108,7 +113,7 @@ test_gen_establish_intro_cell_bad(void *arg)
cell = trn_cell_establish_intro_new();
tt_assert(cell);
ip = service_intro_point_new(NULL);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, NULL);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, NULL);
service_intro_point_free(ip);
expect_log_msg_containing("Unable to make signature for "
"ESTABLISH_INTRO cell.");
diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c
index 7b01809f9..498b9dfcb 100644
--- a/src/test/test_hs_intropoint.c
+++ b/src/test/test_hs_intropoint.c
@@ -45,6 +45,9 @@ new_establish_intro_cell(const char *circ_nonce,
uint8_t buf[RELAY_PAYLOAD_SIZE] = {0};
trn_cell_establish_intro_t *cell = NULL;
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
/* Ensure that *cell_out is NULL such that we can use to check if we need to
* free `cell` in case of an error. */
@@ -54,7 +57,7 @@ new_establish_intro_cell(const char *circ_nonce,
* using this IP object. */
ip = service_intro_point_new(NULL);
tt_assert(ip);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, buf);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, buf);
tt_i64_op(cell_len, OP_GT, 0);
cell_len = trn_cell_establish_intro_parse(&cell, buf, sizeof(buf));
@@ -75,12 +78,15 @@ new_establish_intro_encoded_cell(const char *circ_nonce, uint8_t *cell_out)
{
ssize_t cell_len = 0;
hs_service_intro_point_t *ip = NULL;
+ hs_service_config_t config;
+
+ memset(&config, 0, sizeof(config));
/* Auth key pair is generated in the constructor so we are all set for
* using this IP object. */
ip = service_intro_point_new(NULL);
tt_assert(ip);
- cell_len = hs_cell_build_establish_intro(circ_nonce, ip, cell_out);
+ cell_len = hs_cell_build_establish_intro(circ_nonce, &config, ip, cell_out);
tt_i64_op(cell_len, OP_GT, 0);
done:
diff --git a/src/trunnel/hs/cell_establish_intro.h b/src/trunnel/hs/cell_establish_intro.h
index cc3797fc9..1924d9cab 100644
--- a/src/trunnel/hs/cell_establish_intro.h
+++ b/src/trunnel/hs/cell_establish_intro.h
@@ -11,6 +11,8 @@
struct trn_cell_extension_st;
#define TRUNNEL_SHA3_256_LEN 32
#define TRUNNEL_CELL_EXTENSION_TYPE_DOS 1
+#define TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC 1
+#define TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC 2
#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_TRN_CELL_EXTENSION_DOS_PARAM)
struct trn_cell_extension_dos_param_st {
uint8_t type;
diff --git a/src/trunnel/hs/cell_establish_intro.trunnel b/src/trunnel/hs/cell_establish_intro.trunnel
index 08a110953..e30938f6c 100644
--- a/src/trunnel/hs/cell_establish_intro.trunnel
+++ b/src/trunnel/hs/cell_establish_intro.trunnel
@@ -46,6 +46,10 @@ struct trn_cell_intro_established {
const TRUNNEL_CELL_EXTENSION_TYPE_DOS = 0x01;
+/* DoS Parameter types. */
+const TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC = 0x01;
+const TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC = 0x02;
+
/*
* DoS Parameters Extension. See proposal 305 for more details.
*/
[View Less]
commit aee66c80bd9824650b26c4918fcfeed37ddb9b49
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue Aug 13 11:25:26 2019 -0400
hs-v3: Don't apply DoS defenses if circuit has not been flagged
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/hs_dos.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/feature/hs/hs_dos.c b/src/feature/hs/hs_dos.c
index 34af2b74e..d83c8ee10 100644
--- a/src/feature/hs/hs_dos.c
+++…
[View More] b/src/feature/hs/hs_dos.c
@@ -156,8 +156,10 @@ hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
{
tor_assert(s_intro_circ);
- /* Always allowed if the defense is disabled. */
- if (!param_introduce_defense_enabled) {
+ /* Allow to send the cell if the DoS defenses are disabled on the circuit.
+ * This can be set by the consensus, the ESTABLISH_INTRO cell extension or
+ * the hardcoded values in tor code. */
+ if (!s_intro_circ->introduce2_dos_defense_enabled) {
return true;
}
[View Less]
commit 1c554334ac6a7d39c69ec8dd26702a613bb899b4
Author: David Goulet <dgoulet(a)torproject.org>
Date: Wed Aug 14 10:41:40 2019 -0400
test: Adapt HS DoS test to use latest parameter
We added a flag on the circuit to know if the DoS defenses are enabled or not.
Before, it was solely the consensus parameter.
Part of #30924
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/test/test_hs_dos.c | 1 +
1 file changed, 1 insertion(+)
diff -…
[View More]-git a/src/test/test_hs_dos.c b/src/test/test_hs_dos.c
index e2b211d5d..f92d953fa 100644
--- a/src/test/test_hs_dos.c
+++ b/src/test/test_hs_dos.c
@@ -57,6 +57,7 @@ test_can_send_intro2(void *arg)
/* Make that circuit a service intro point. */
circuit_change_purpose(TO_CIRCUIT(or_circ), CIRCUIT_PURPOSE_INTRO_POINT);
+ or_circ->introduce2_dos_defense_enabled = 1;
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
token_bucket_ctr_init(&or_circ->introduce2_bucket,
hs_dos_get_intro2_rate_param(),
[View Less]
commit 82639a8c7bf59bb6ae73218cd78cee07b6586d96
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue Aug 20 08:35:21 2019 -0400
hs-v3: Move to hs_dos.c INTRO2 defenses initialization
A bit cleaner especially that the next commit(s) will make the consensus param
interface private to hs_dos.c so we expose as little as we can outside of the
subsystem.
Part of #30924
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/feature/hs/…
[View More]hs_dos.c | 15 +++++++++++++++
src/feature/hs/hs_dos.h | 1 +
src/feature/hs/hs_intropoint.c | 11 +++--------
src/feature/rend/rendmid.c | 3 +--
4 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/src/feature/hs/hs_dos.c b/src/feature/hs/hs_dos.c
index d83c8ee10..0eb306977 100644
--- a/src/feature/hs/hs_dos.c
+++ b/src/feature/hs/hs_dos.c
@@ -135,6 +135,21 @@ hs_dos_get_intro2_enabled_param(void)
return (unsigned int) param_introduce_defense_enabled;
}
+/* Initialize the INTRODUCE2 token bucket for the DoS defenses using the
+ * consensus/default values. We might get a cell extension that changes those
+ * later but if we don't, the default or consensus parameters are used. */
+void
+hs_dos_setup_default_intro2_defenses(or_circuit_t *circ)
+{
+ tor_assert(circ);
+
+ circ->introduce2_dos_defense_enabled = get_param_intro_dos_enabled(NULL);
+ token_bucket_ctr_init(&circ->introduce2_bucket,
+ get_param_rate_per_sec(NULL),
+ get_param_burst_per_sec(NULL),
+ (uint32_t) approx_time());
+}
+
/* Called when the consensus has changed. We might have new consensus
* parameters to look at. */
void
diff --git a/src/feature/hs/hs_dos.h b/src/feature/hs/hs_dos.h
index ee7b697c7..1d2dd67d0 100644
--- a/src/feature/hs/hs_dos.h
+++ b/src/feature/hs/hs_dos.h
@@ -22,6 +22,7 @@ void hs_dos_consensus_has_changed(const networkstatus_t *ns);
/* Introduction Point. */
bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ);
+void hs_dos_setup_default_intro2_defenses(or_circuit_t *circ);
unsigned int hs_dos_get_intro2_enabled_param(void);
uint32_t hs_dos_get_intro2_rate_param(void);
diff --git a/src/feature/hs/hs_intropoint.c b/src/feature/hs/hs_intropoint.c
index bafd3d1f4..fc7d96194 100644
--- a/src/feature/hs/hs_intropoint.c
+++ b/src/feature/hs/hs_intropoint.c
@@ -298,14 +298,9 @@ handle_verified_establish_intro_cell(or_circuit_t *circ,
get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO,
parsed_cell);
- /* Initialize the INTRODUCE2 token bucket for the DoS defenses using the
- * consensus/default values. We might get a cell extension that changes
- * those but if we don't, the default or consensus parameters are used. */
- circ->introduce2_dos_defense_enabled = hs_dos_get_intro2_enabled_param();
- token_bucket_ctr_init(&circ->introduce2_bucket,
- hs_dos_get_intro2_rate_param(),
- hs_dos_get_intro2_burst_param(),
- (uint32_t) approx_time());
+ /* Setup INTRODUCE2 defenses on the circuit. Must be done before parsing the
+ * cell extension that can possibly change the defenses' values. */
+ hs_dos_setup_default_intro2_defenses(circ);
/* Handle cell extension if any. */
handle_establish_intro_cell_extensions(parsed_cell, circ);
diff --git a/src/feature/rend/rendmid.c b/src/feature/rend/rendmid.c
index ef2a44c40..be3d66fc4 100644
--- a/src/feature/rend/rendmid.c
+++ b/src/feature/rend/rendmid.c
@@ -117,8 +117,7 @@ rend_mid_establish_intro_legacy(or_circuit_t *circ, const uint8_t *request,
/* Now, set up this circuit. */
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
hs_circuitmap_register_intro_circ_v2_relay_side(circ, (uint8_t *)pk_digest);
- token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(),
- hs_dos_get_intro2_burst(), (uint32_t) approx_time());
+ hs_dos_setup_default_intro2_defenses(circ);
log_info(LD_REND,
"Established introduction point on circuit %u for service %s",
[View Less]