diff options
author | David Goulet <dgoulet@torproject.org> | 2019-08-12 11:59:51 -0400 |
---|---|---|
committer | George Kadianakis <desnacked@riseup.net> | 2019-08-26 15:53:46 +0300 |
commit | d692c5fd0368da79f83307f6f023ae5793472e1a (patch) | |
tree | 13c27ca06b30caa7ab1a8a6a7240db1255a71c7f /src | |
parent | 7faf10495fa50528fc8a9c45e729b9bc284fab19 (diff) | |
download | tor-d692c5fd0368da79f83307f6f023ae5793472e1a.tar.gz tor-d692c5fd0368da79f83307f6f023ae5793472e1a.zip |
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.
Signed-off-by: David Goulet <dgoulet@torproject.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/feature/hs/hs_cell.c | 111 | ||||
-rw-r--r-- | src/feature/hs/hs_cell.h | 1 | ||||
-rw-r--r-- | src/feature/hs/hs_circuit.c | 2 | ||||
-rw-r--r-- | src/feature/hs/hs_service.c | 4 | ||||
-rw-r--r-- | src/feature/hs/hs_service.h | 4 | ||||
-rw-r--r-- | src/test/test_hs_cell.c | 9 | ||||
-rw-r--r-- | src/test/test_hs_intropoint.c | 10 | ||||
-rw-r--r-- | src/trunnel/hs/cell_establish_intro.h | 2 | ||||
-rw-r--r-- | 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 69f1ccbef4..a67af1e9ad 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 9569de535e..5fb416c2f4 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 a6e86c5ab3..66f81189bf 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 bbfade4d14..ef9bce98e4 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 77f6527c20..c4bbb293bb 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 cdcbe23e69..874c04b202 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 7b01809f96..498b9dfcbe 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 cc3797fc99..1924d9cab6 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 08a110953c..e30938f6c2 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. */ |