aboutsummaryrefslogtreecommitdiff
path: root/src/feature/hs/hs_cell.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs/hs_cell.c')
-rw-r--r--src/feature/hs/hs_cell.c189
1 files changed, 160 insertions, 29 deletions
diff --git a/src/feature/hs/hs_cell.c b/src/feature/hs/hs_cell.c
index 613ffe7260..52bd663200 100644
--- a/src/feature/hs/hs_cell.c
+++ b/src/feature/hs/hs_cell.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017-2019, The Tor Project, Inc. */
+/* Copyright (c) 2017-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -24,7 +24,7 @@
#include "trunnel/hs/cell_introduce1.h"
#include "trunnel/hs/cell_rendezvous.h"
-/* Compute the MAC of an INTRODUCE cell in mac_out. The encoded_cell param is
+/** Compute the MAC of an INTRODUCE cell in mac_out. The encoded_cell param is
* the cell content up to the ENCRYPTED section of length encoded_cell_len.
* The encrypted param is the start of the ENCRYPTED section of length
* encrypted_len. The mac_key is the key needed for the computation of the MAC
@@ -67,7 +67,7 @@ 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
+/** 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. */
@@ -101,7 +101,7 @@ get_introduce2_key_material(const ed25519_public_key_t *auth_key,
return keys;
}
-/* Using the given encryption key, decrypt the encrypted_section of length
+/** Using the given encryption key, decrypt the encrypted_section of length
* encrypted_section_len of an INTRODUCE2 cell and return a newly allocated
* buffer containing the decrypted data. On decryption failure, NULL is
* returned. */
@@ -136,7 +136,7 @@ decrypt_introduce2(const uint8_t *enc_key, const uint8_t *encrypted_section,
return decrypted;
}
-/* Given a pointer to the decrypted data of the ENCRYPTED section of an
+/** Given a pointer to the decrypted data of the ENCRYPTED section of an
* INTRODUCE2 cell of length decrypted_len, parse and validate the cell
* content. Return a newly allocated cell structure or NULL on error. The
* circuit and service object are only used for logging purposes. */
@@ -188,7 +188,7 @@ parse_introduce2_encrypted(const uint8_t *decrypted_data,
return NULL;
}
-/* Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
+/** Build a legacy ESTABLISH_INTRO cell with the given circuit nonce and RSA
* encryption key. 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 a negative value and cell_out is untouched. */
@@ -210,7 +210,7 @@ build_legacy_establish_intro(const char *circ_nonce, crypto_pk_t *enc_key,
return cell_len;
}
-/* Parse an INTRODUCE2 cell from payload of size payload_len for the given
+/** Parse an INTRODUCE2 cell from payload of size payload_len for the given
* service and circuit which are used only for logging purposes. The resulting
* parsed cell is put in cell_ptr_out.
*
@@ -249,7 +249,7 @@ parse_introduce2_cell(const hs_service_t *service,
return -1;
}
-/* Set the onion public key onion_pk in cell, the encrypted section of an
+/** Set the onion public key onion_pk in cell, the encrypted section of an
* INTRODUCE1 cell. */
static void
introduce1_set_encrypted_onion_key(trn_cell_introduce_encrypted_t *cell,
@@ -266,7 +266,7 @@ introduce1_set_encrypted_onion_key(trn_cell_introduce_encrypted_t *cell,
trn_cell_introduce_encrypted_getlen_onion_key(cell));
}
-/* Set the link specifiers in lspecs in cell, the encrypted section of an
+/** Set the link specifiers in lspecs in cell, the encrypted section of an
* INTRODUCE1 cell. */
static void
introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
@@ -286,7 +286,7 @@ introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
trn_cell_introduce_encrypted_add_nspecs(cell, ls));
}
-/* Set padding in the enc_cell only if needed that is the total length of both
+/** Set padding in the enc_cell only if needed that is the total length of both
* sections are below the mininum required for an INTRODUCE1 cell. */
static void
introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
@@ -306,7 +306,7 @@ introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
}
}
-/* Encrypt the ENCRYPTED payload and encode it in the cell using the enc_cell
+/** Encrypt the ENCRYPTED payload and encode it in the cell using the enc_cell
* and the INTRODUCE1 data.
*
* This can't fail but it is very important that the caller sets every field
@@ -394,7 +394,7 @@ introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
tor_free(encrypted);
}
-/* Using the INTRODUCE1 data, setup the ENCRYPTED section in cell. This means
+/** Using the INTRODUCE1 data, setup the ENCRYPTED section in cell. This means
* set it, encrypt it and encode it. */
static void
introduce1_set_encrypted(trn_cell_introduce1_t *cell,
@@ -435,7 +435,7 @@ introduce1_set_encrypted(trn_cell_introduce1_t *cell,
trn_cell_introduce_encrypted_free(enc_cell);
}
-/* Set the authentication key in the INTRODUCE1 cell from the given data. */
+/** Set the authentication key in the INTRODUCE1 cell from the given data. */
static void
introduce1_set_auth_key(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
@@ -451,7 +451,7 @@ introduce1_set_auth_key(trn_cell_introduce1_t *cell,
data->auth_pk->pubkey, trn_cell_introduce1_getlen_auth_key(cell));
}
-/* Set the legacy ID field in the INTRODUCE1 cell from the given data. */
+/** Set the legacy ID field in the INTRODUCE1 cell from the given data. */
static void
introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
const hs_cell_introduce1_data_t *data)
@@ -473,26 +473,150 @@ 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. Return 0 on success, -1 on failure. (Right now, failure is only
+ * possible if there is a bug.) */
+static int
+build_establish_intro_dos_extension(const hs_service_config_t *service_config,
+ trn_cell_extension_t *extensions)
+{
+ ssize_t ret;
+ size_t dos_ext_encoded_len;
+ uint8_t *field_array;
+ trn_cell_extension_field_t *field = NULL;
+ trn_cell_extension_dos_t *dos_ext = NULL;
+
+ 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. */
+ ret = trn_cell_extension_dos_encoded_len(dos_ext);
+ if (BUG(ret <= 0)) {
+ goto err;
+ }
+ dos_ext_encoded_len = ret;
+ /* 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);
+ if (BUG(ret <= 0)) {
+ goto err;
+ }
+ tor_assert(ret == (ssize_t) 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);
+
+ return 0;
+
+ err:
+ trn_cell_extension_field_free(field);
+ trn_cell_extension_dos_free(dos_ext);
+ return -1;
+}
+
/* ========== */
/* Public API */
/* ========== */
-/* Build an ESTABLISH_INTRO cell with the given circuit nonce and intro point
+/** 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)
+{
+ int ret;
+ 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. */
+ ret = build_establish_intro_dos_extension(service_config, extensions);
+ if (ret < 0) {
+ /* Return no extensions on error. */
+ goto end;
+ }
+ }
+
+ end:
+ 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
* a negative value and cell_out is untouched. This function also supports
* 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 +629,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);
@@ -600,7 +725,7 @@ hs_cell_build_establish_intro(const char *circ_nonce,
return cell_len;
}
-/* Parse the INTRO_ESTABLISHED cell in the payload of size payload_len. If we
+/** Parse the INTRO_ESTABLISHED cell in the payload of size payload_len. If we
* are successful at parsing it, return the length of the parsed cell else a
* negative value on error. */
ssize_t
@@ -622,7 +747,7 @@ hs_cell_parse_intro_established(const uint8_t *payload, size_t payload_len)
return ret;
}
-/* Parse the INTRODUCE2 cell using data which contains everything we need to
+/** 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
* service and circ are only used for logging purposes. */
@@ -758,7 +883,14 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
idx < trn_cell_introduce_encrypted_get_nspec(enc_cell); idx++) {
link_specifier_t *lspec =
trn_cell_introduce_encrypted_get_nspecs(enc_cell, idx);
- smartlist_add(data->link_specifiers, hs_link_specifier_dup(lspec));
+ if (BUG(!lspec)) {
+ goto done;
+ }
+ link_specifier_t *lspec_dup = link_specifier_dup(lspec);
+ if (BUG(!lspec_dup)) {
+ goto done;
+ }
+ smartlist_add(data->link_specifiers, lspec_dup);
}
/* Success. */
@@ -776,7 +908,7 @@ hs_cell_parse_introduce2(hs_cell_introduce2_data_t *data,
return ret;
}
-/* Build a RENDEZVOUS1 cell with the given rendezvous cookie and handshake
+/** Build a RENDEZVOUS1 cell with the given rendezvous cookie and handshake
* info. The encoded cell is put in cell_out and the length of the data is
* returned. This can't fail. */
ssize_t
@@ -810,7 +942,7 @@ hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
return cell_len;
}
-/* Build an INTRODUCE1 cell from the given data. The encoded cell is put in
+/** Build an INTRODUCE1 cell from the given data. The encoded cell is put in
* cell_out which must be of at least size RELAY_PAYLOAD_SIZE. On success, the
* encoded length is returned else a negative value and the content of
* cell_out should be ignored. */
@@ -851,7 +983,7 @@ hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
return cell_len;
}
-/* Build an ESTABLISH_RENDEZVOUS cell from the given rendezvous_cookie. The
+/** Build an ESTABLISH_RENDEZVOUS cell from the given rendezvous_cookie. The
* encoded cell is put in cell_out which must be of at least
* RELAY_PAYLOAD_SIZE. On success, the encoded length is returned and the
* caller should clear up the content of the cell.
@@ -868,7 +1000,7 @@ hs_cell_build_establish_rendezvous(const uint8_t *rendezvous_cookie,
return HS_REND_COOKIE_LEN;
}
-/* Handle an INTRODUCE_ACK cell encoded in payload of length payload_len.
+/** Handle an INTRODUCE_ACK cell encoded in payload of length payload_len.
* Return the status code on success else a negative value if the cell as not
* decodable. */
int
@@ -903,7 +1035,7 @@ hs_cell_parse_introduce_ack(const uint8_t *payload, size_t payload_len)
return ret;
}
-/* Handle a RENDEZVOUS2 cell encoded in payload of length payload_len. On
+/** Handle a RENDEZVOUS2 cell encoded in payload of length payload_len. On
* success, handshake_info contains the data in the HANDSHAKE_INFO field, and
* 0 is returned. On error, a negative value is returned. */
int
@@ -935,7 +1067,7 @@ hs_cell_parse_rendezvous2(const uint8_t *payload, size_t payload_len,
return ret;
}
-/* Clear the given INTRODUCE1 data structure data. */
+/** Clear the given INTRODUCE1 data structure data. */
void
hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data)
{
@@ -949,4 +1081,3 @@ hs_cell_introduce1_data_clear(hs_cell_introduce1_data_t *data)
/* The data object has no ownership of any members. */
memwipe(data, 0, sizeof(hs_cell_introduce1_data_t));
}
-