commit f98f7ca89865d1a477fc65165b92c86b9d8437fa Merge: f406b9df0 490ae26b2 Author: David Goulet dgoulet@torproject.org Date: Fri Jan 19 16:21:55 2018 -0500
Merge branch 'bug24895_029_02' into bug24895_031_02
changes/bug24895 | 8 ++++++++ src/or/hs_common.c | 17 +++++++++++++++++ src/or/hs_common.h | 1 + src/or/hs_service.c | 1 + src/or/rendservice.c | 10 ++++++++-- 5 files changed, 35 insertions(+), 2 deletions(-)
diff --cc src/or/hs_common.c index 7cef5a8e2,000000000..c9af3f688 mode 100644,000000..100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@@ -1,346 -1,0 +1,363 @@@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_common.c + * \brief Contains code shared between different HS protocol version as well + * as useful data structures and accessors used by other subsystems. + * The rendcommon.c should only contains code relating to the v2 + * protocol. + **/ + +#define HS_COMMON_PRIVATE + +#include "or.h" + +#include "config.h" +#include "networkstatus.h" +#include "hs_common.h" +#include "rendcommon.h" + +/* Make sure that the directory for <b>service</b> is private, using the config + * <b>username</b>. + * If <b>create</b> is true: + * - if the directory exists, change permissions if needed, + * - if the directory does not exist, create it with the correct permissions. + * If <b>create</b> is false: + * - if the directory exists, check permissions, + * - if the directory does not exist, check if we think we can create it. + * Return 0 on success, -1 on failure. */ +int +hs_check_service_private_dir(const char *username, const char *path, + unsigned int dir_group_readable, + unsigned int create) +{ + cpd_check_t check_opts = CPD_NONE; + + tor_assert(path); + + if (create) { + check_opts |= CPD_CREATE; + } else { + check_opts |= CPD_CHECK_MODE_ONLY; + check_opts |= CPD_CHECK; + } + if (dir_group_readable) { + check_opts |= CPD_GROUP_READ; + } + /* Check/create directory */ + if (check_private_dir(path, check_opts, username) < 0) { + return -1; + } + return 0; +} + +/** Get the default HS time period length in minutes from the consensus. */ +STATIC uint64_t +get_time_period_length(void) +{ + int32_t time_period_length = networkstatus_get_param(NULL, "hsdir_interval", + HS_TIME_PERIOD_LENGTH_DEFAULT, + HS_TIME_PERIOD_LENGTH_MIN, + HS_TIME_PERIOD_LENGTH_MAX); + /* Make sure it's a positive value. */ + tor_assert(time_period_length >= 0); + /* uint64_t will always be able to contain a int32_t */ + return (uint64_t) time_period_length; +} + +/** Get the HS time period number at time <b>now</b> */ +STATIC uint64_t +get_time_period_num(time_t now) +{ + uint64_t time_period_num; + uint64_t time_period_length = get_time_period_length(); + uint64_t minutes_since_epoch = now / 60; + + /* Now subtract half a day to fit the prop224 time period schedule (see + * section [TIME-PERIODS]). */ + tor_assert(minutes_since_epoch > HS_TIME_PERIOD_ROTATION_OFFSET); + minutes_since_epoch -= HS_TIME_PERIOD_ROTATION_OFFSET; + + /* Calculate the time period */ + time_period_num = minutes_since_epoch / time_period_length; + return time_period_num; +} + +/** Get the number of the _upcoming_ HS time period, given that the current + * time is <b>now</b>. */ +uint64_t +hs_get_next_time_period_num(time_t now) +{ + return get_time_period_num(now) + 1; +} + +/* Create a new rend_data_t for a specific given <b>version</b>. + * Return a pointer to the newly allocated data structure. */ +static rend_data_t * +rend_data_alloc(uint32_t version) +{ + rend_data_t *rend_data = NULL; + + switch (version) { + case HS_VERSION_TWO: + { + rend_data_v2_t *v2 = tor_malloc_zero(sizeof(*v2)); + v2->base_.version = HS_VERSION_TWO; + v2->base_.hsdirs_fp = smartlist_new(); + rend_data = &v2->base_; + break; + } + default: + tor_assert(0); + break; + } + + return rend_data; +} + +/** Free all storage associated with <b>data</b> */ +void +rend_data_free(rend_data_t *data) +{ + if (!data) { + return; + } + /* By using our allocation function, this should always be set. */ + tor_assert(data->hsdirs_fp); + /* Cleanup the HSDir identity digest. */ + SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(data->hsdirs_fp); + /* Depending on the version, cleanup. */ + switch (data->version) { + case HS_VERSION_TWO: + { + rend_data_v2_t *v2_data = TO_REND_DATA_V2(data); + tor_free(v2_data); + break; + } + default: + tor_assert(0); + } +} + +/* Allocate and return a deep copy of <b>data</b>. */ +rend_data_t * +rend_data_dup(const rend_data_t *data) +{ + rend_data_t *data_dup = NULL; + smartlist_t *hsdirs_fp = smartlist_new(); + + tor_assert(data); + tor_assert(data->hsdirs_fp); + + SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp, + smartlist_add(hsdirs_fp, tor_memdup(fp, DIGEST_LEN))); + + switch (data->version) { + case HS_VERSION_TWO: + { + rend_data_v2_t *v2_data = tor_memdup(TO_REND_DATA_V2(data), + sizeof(*v2_data)); + data_dup = &v2_data->base_; + data_dup->hsdirs_fp = hsdirs_fp; + break; + } + default: + tor_assert(0); + break; + } + + return data_dup; +} + +/* Compute the descriptor ID for each HS descriptor replica and save them. A + * valid onion address must be present in the <b>rend_data</b>. + * + * Return 0 on success else -1. */ +static int +compute_desc_id(rend_data_t *rend_data) +{ + int ret = 0; + unsigned replica; + time_t now = time(NULL); + + tor_assert(rend_data); + + switch (rend_data->version) { + case HS_VERSION_TWO: + { + rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data); + /* Compute descriptor ID for each replicas. */ + for (replica = 0; replica < ARRAY_LENGTH(v2_data->descriptor_id); + replica++) { + ret = rend_compute_v2_desc_id(v2_data->descriptor_id[replica], + v2_data->onion_address, + v2_data->descriptor_cookie, + now, replica); + if (ret < 0) { + goto end; + } + } + break; + } + default: + tor_assert(0); + } + + end: + return ret; +} + +/* Allocate and initialize a rend_data_t object for a service using the + * provided arguments. All arguments are optional (can be NULL), except from + * <b>onion_address</b> which MUST be set. The <b>pk_digest</b> is the hash of + * the service private key. The <b>cookie</b> is the rendezvous cookie and + * <b>auth_type</b> is which authentiation this service is configured with. + * + * Return a valid rend_data_t pointer. This only returns a version 2 object of + * rend_data_t. */ +rend_data_t * +rend_data_service_create(const char *onion_address, const char *pk_digest, + const uint8_t *cookie, rend_auth_type_t auth_type) +{ + /* Create a rend_data_t object for version 2. */ + rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO); + rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL); + + if (pk_digest) { + memcpy(v2->rend_pk_digest, pk_digest, sizeof(v2->rend_pk_digest)); + } + if (cookie) { + memcpy(rend_data->rend_cookie, cookie, sizeof(rend_data->rend_cookie)); + } + + strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address)); + v2->auth_type = auth_type; + + return rend_data; +} + +/* Allocate and initialize a rend_data_t object for a client request using the + * given arguments. Either an onion address or a descriptor ID is needed. Both + * can be given but in this case only the onion address will be used to make + * the descriptor fetch. The <b>cookie</b> is the rendezvous cookie and + * <b>auth_type</b> is which authentiation the service is configured with. + * + * Return a valid rend_data_t pointer or NULL on error meaning the + * descriptor IDs couldn't be computed from the given data. */ +rend_data_t * +rend_data_client_create(const char *onion_address, const char *desc_id, + const char *cookie, rend_auth_type_t auth_type) +{ + /* Create a rend_data_t object for version 2. */ + rend_data_t *rend_data = rend_data_alloc(HS_VERSION_TWO); + rend_data_v2_t *v2= TO_REND_DATA_V2(rend_data); + + /* We need at least one else the call is wrong. */ + tor_assert(onion_address != NULL || desc_id != NULL); + + if (cookie) { + memcpy(v2->descriptor_cookie, cookie, sizeof(v2->descriptor_cookie)); + } + if (desc_id) { + memcpy(v2->desc_id_fetch, desc_id, sizeof(v2->desc_id_fetch)); + } + if (onion_address) { + strlcpy(v2->onion_address, onion_address, sizeof(v2->onion_address)); + if (compute_desc_id(rend_data) < 0) { + goto error; + } + } + + v2->auth_type = auth_type; + + return rend_data; + + error: + rend_data_free(rend_data); + return NULL; +} + +/* Return the onion address from the rend data. Depending on the version, + * the size of the address can vary but it's always NUL terminated. */ +const char * +rend_data_get_address(const rend_data_t *rend_data) +{ + tor_assert(rend_data); + + switch (rend_data->version) { + case HS_VERSION_TWO: + return TO_REND_DATA_V2(rend_data)->onion_address; + default: + /* We should always have a supported version. */ + tor_assert(0); + } +} + +/* Return the descriptor ID for a specific replica number from the rend + * data. The returned data is a binary digest and depending on the version its + * size can vary. The size of the descriptor ID is put in <b>len_out</b> if + * non NULL. */ +const char * +rend_data_get_desc_id(const rend_data_t *rend_data, uint8_t replica, + size_t *len_out) +{ + tor_assert(rend_data); + + switch (rend_data->version) { + case HS_VERSION_TWO: + tor_assert(replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS); + if (len_out) { + *len_out = DIGEST_LEN; + } + return TO_REND_DATA_V2(rend_data)->descriptor_id[replica]; + default: + /* We should always have a supported version. */ + tor_assert(0); + } +} + +/* Return the public key digest using the given <b>rend_data</b>. The size of + * the digest is put in <b>len_out</b> (if set) which can differ depending on + * the version. */ +const uint8_t * +rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out) +{ + tor_assert(rend_data); + + switch (rend_data->version) { + case HS_VERSION_TWO: + { + const rend_data_v2_t *v2_data = TO_REND_DATA_V2(rend_data); + if (len_out) { + *len_out = sizeof(v2_data->rend_pk_digest); + } + return (const uint8_t *) v2_data->rend_pk_digest; + } + default: + /* We should always have a supported version. */ + tor_assert(0); + } +} + ++/* Default, minimum and maximum values for the maximum rendezvous failures ++ * consensus parameter. */ ++#define MAX_REND_FAILURES_DEFAULT 2 ++#define MAX_REND_FAILURES_MIN 1 ++#define MAX_REND_FAILURES_MAX 10 ++ ++/** How many times will a hidden service operator attempt to connect to ++ * a requested rendezvous point before giving up? */ ++int ++hs_get_service_max_rend_failures(void) ++{ ++ return networkstatus_get_param(NULL, "hs_service_max_rdv_failures", ++ MAX_REND_FAILURES_DEFAULT, ++ MAX_REND_FAILURES_MIN, ++ MAX_REND_FAILURES_MAX); ++} ++ diff --cc src/or/hs_common.h index a8fded652,000000000..7eef5fc97 mode 100644,000000..100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@@ -1,86 -1,0 +1,87 @@@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_common.h + * \brief Header file containing common data for the whole HS subsytem. + **/ + +#ifndef TOR_HS_COMMON_H +#define TOR_HS_COMMON_H + +#include "or.h" + +/* Protocol version 2. Use this instead of hardcoding "2" in the code base, + * this adds a clearer semantic to the value when used. */ +#define HS_VERSION_TWO 2 +/* Version 3 of the protocol (prop224). */ +#define HS_VERSION_THREE 3 + +/** Try to maintain this many intro points per service by default. */ +#define NUM_INTRO_POINTS_DEFAULT 3 +/** Maximum number of intro points per service. */ +#define NUM_INTRO_POINTS_MAX 10 +/** Number of extra intro points we launch if our set of intro nodes is empty. + * See proposal 155, section 4. */ +#define NUM_INTRO_POINTS_EXTRA 2 + +/** If we can't build our intro circuits, don't retry for this long. */ +#define INTRO_CIRC_RETRY_PERIOD (60*5) +/** Don't try to build more than this many circuits before giving up for a + * while.*/ +#define MAX_INTRO_CIRCS_PER_PERIOD 10 +/** How many times will a hidden service operator attempt to connect to a + * requested rendezvous point before giving up? */ +#define MAX_REND_FAILURES 1 +/** How many seconds should we spend trying to connect to a requested + * rendezvous point before giving up? */ +#define MAX_REND_TIMEOUT 30 + +/* String prefix for the signature of ESTABLISH_INTRO */ +#define ESTABLISH_INTRO_SIG_PREFIX "Tor establish-intro cell v1" + +/* The default HS time period length */ +#define HS_TIME_PERIOD_LENGTH_DEFAULT 1440 /* 1440 minutes == one day */ +/* The minimum time period length as seen in prop224 section [TIME-PERIODS] */ +#define HS_TIME_PERIOD_LENGTH_MIN 30 /* minutes */ +/* The minimum time period length as seen in prop224 section [TIME-PERIODS] */ +#define HS_TIME_PERIOD_LENGTH_MAX (60 * 24 * 10) /* 10 days or 14400 minutes */ +/* The time period rotation offset as seen in prop224 section [TIME-PERIODS] */ +#define HS_TIME_PERIOD_ROTATION_OFFSET (12 * 60) /* minutes */ + +int hs_check_service_private_dir(const char *username, const char *path, + unsigned int dir_group_readable, + unsigned int create); ++int hs_get_service_max_rend_failures(void); + +void rend_data_free(rend_data_t *data); +rend_data_t *rend_data_dup(const rend_data_t *data); +rend_data_t *rend_data_client_create(const char *onion_address, + const char *desc_id, + const char *cookie, + rend_auth_type_t auth_type); +rend_data_t *rend_data_service_create(const char *onion_address, + const char *pk_digest, + const uint8_t *cookie, + rend_auth_type_t auth_type); +const char *rend_data_get_address(const rend_data_t *rend_data); +const char *rend_data_get_desc_id(const rend_data_t *rend_data, + uint8_t replica, size_t *len_out); +const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data, + size_t *len_out); + +uint64_t hs_get_next_time_period_num(time_t now); + +#ifdef HS_COMMON_PRIVATE + +#ifdef TOR_UNIT_TESTS + +STATIC uint64_t get_time_period_length(void); +STATIC uint64_t get_time_period_num(time_t now); + +#endif /* TOR_UNIT_TESTS */ + +#endif /* HS_COMMON_PRIVATE */ + +#endif /* TOR_HS_COMMON_H */ + diff --cc src/or/hs_service.c index 205ef11c9,000000000..b3eec1304 mode 100644,000000..100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@@ -1,174 -1,0 +1,175 @@@ +/* Copyright (c) 2016-2017, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file hs_service.c + * \brief Implement next generation hidden service functionality + **/ + +#include "or.h" +#include "relay.h" +#include "rendservice.h" +#include "circuitlist.h" +#include "circpathbias.h" ++#include "networkstatus.h" + +#include "hs_intropoint.h" +#include "hs_service.h" +#include "hs_common.h" + +#include "hs/cell_establish_intro.h" +#include "hs/cell_common.h" + +/* XXX We don't currently use these functions, apart from generating unittest + data. When we start implementing the service-side support for prop224 we + should revisit these functions and use them. */ + +/** Given an ESTABLISH_INTRO <b>cell</b>, encode it and place its payload in + * <b>buf_out</b> which has size <b>buf_out_len</b>. Return the number of + * bytes written, or a negative integer if there was an error. */ +ssize_t +get_establish_intro_payload(uint8_t *buf_out, size_t buf_out_len, + const trn_cell_establish_intro_t *cell) +{ + ssize_t bytes_used = 0; + + if (buf_out_len < RELAY_PAYLOAD_SIZE) { + return -1; + } + + bytes_used = trn_cell_establish_intro_encode(buf_out, buf_out_len, + cell); + return bytes_used; +} + +/* Set the cell extensions of <b>cell</b>. */ +static void +set_trn_cell_extensions(trn_cell_establish_intro_t *cell) +{ + trn_cell_extension_t *trn_cell_extensions = trn_cell_extension_new(); + + /* For now, we don't use extensions at all. */ + trn_cell_extensions->num = 0; /* It's already zeroed, but be explicit. */ + trn_cell_establish_intro_set_extensions(cell, trn_cell_extensions); +} + +/** Given the circuit handshake info in <b>circuit_key_material</b>, create and + * return an ESTABLISH_INTRO cell. Return NULL if something went wrong. The + * returned cell is allocated on the heap and it's the responsibility of the + * caller to free it. */ +trn_cell_establish_intro_t * +generate_establish_intro_cell(const uint8_t *circuit_key_material, + size_t circuit_key_material_len) +{ + trn_cell_establish_intro_t *cell = NULL; + ssize_t encoded_len; + + log_warn(LD_GENERAL, + "Generating ESTABLISH_INTRO cell (key_material_len: %u)", + (unsigned) circuit_key_material_len); + + /* Generate short-term keypair for use in ESTABLISH_INTRO */ + ed25519_keypair_t key_struct; + if (ed25519_keypair_generate(&key_struct, 0) < 0) { + goto err; + } + + cell = trn_cell_establish_intro_new(); + + /* Set AUTH_KEY_TYPE: 2 means ed25519 */ + trn_cell_establish_intro_set_auth_key_type(cell, + HS_INTRO_AUTH_KEY_TYPE_ED25519); + + /* Set AUTH_KEY_LEN field */ + /* Must also set byte-length of AUTH_KEY to match */ + int auth_key_len = ED25519_PUBKEY_LEN; + trn_cell_establish_intro_set_auth_key_len(cell, auth_key_len); + trn_cell_establish_intro_setlen_auth_key(cell, auth_key_len); + + /* Set AUTH_KEY field */ + uint8_t *auth_key_ptr = trn_cell_establish_intro_getarray_auth_key(cell); + memcpy(auth_key_ptr, key_struct.pubkey.pubkey, auth_key_len); + + /* No cell extensions needed */ + set_trn_cell_extensions(cell); + + /* Set signature size. + We need to do this up here, because _encode() needs it and we need to call + _encode() to calculate the MAC and signature. + */ + int sig_len = ED25519_SIG_LEN; + trn_cell_establish_intro_set_sig_len(cell, sig_len); + trn_cell_establish_intro_setlen_sig(cell, sig_len); + + /* XXX How to make this process easier and nicer? */ + + /* Calculate the cell MAC (aka HANDSHAKE_AUTH). */ + { + /* To calculate HANDSHAKE_AUTH, we dump the cell in bytes, and then derive + the MAC from it. */ + uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0}; + uint8_t mac[TRUNNEL_SHA3_256_LEN]; + + encoded_len = trn_cell_establish_intro_encode(cell_bytes_tmp, + sizeof(cell_bytes_tmp), + cell); + if (encoded_len < 0) { + log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell."); + goto err; + } + + /* sanity check */ + tor_assert(encoded_len > ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN); + + /* Calculate MAC of all fields before HANDSHAKE_AUTH */ + crypto_mac_sha3_256(mac, sizeof(mac), + circuit_key_material, circuit_key_material_len, + cell_bytes_tmp, + encoded_len - + (ED25519_SIG_LEN + 2 + TRUNNEL_SHA3_256_LEN)); + /* Write the MAC to the cell */ + uint8_t *handshake_ptr = + trn_cell_establish_intro_getarray_handshake_mac(cell); + memcpy(handshake_ptr, mac, sizeof(mac)); + } + + /* Calculate the cell signature */ + { + /* To calculate the sig we follow the same procedure as above. We first + dump the cell up to the sig, and then calculate the sig */ + uint8_t cell_bytes_tmp[RELAY_PAYLOAD_SIZE] = {0}; + ed25519_signature_t sig; + + encoded_len = trn_cell_establish_intro_encode(cell_bytes_tmp, + sizeof(cell_bytes_tmp), + cell); + if (encoded_len < 0) { + log_warn(LD_OR, "Unable to pre-encode ESTABLISH_INTRO cell (2)."); + goto err; + } + + tor_assert(encoded_len > ED25519_SIG_LEN); + + if (ed25519_sign_prefixed(&sig, + cell_bytes_tmp, + encoded_len - + (ED25519_SIG_LEN + sizeof(cell->sig_len)), + ESTABLISH_INTRO_SIG_PREFIX, + &key_struct)) { + log_warn(LD_BUG, "Unable to gen signature for ESTABLISH_INTRO cell."); + goto err; + } + + /* And write the signature to the cell */ + uint8_t *sig_ptr = trn_cell_establish_intro_getarray_sig(cell); + memcpy(sig_ptr, sig.sig, sig_len); + } + + /* We are done! Return the cell! */ + return cell; + + err: + trn_cell_establish_intro_free(cell); + return NULL; +} + diff --cc src/or/rendservice.c index b1e8a2f0c,da200d138..2a3594918 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@@ -2159,7 -2041,8 +2159,8 @@@ rend_service_receive_introduction(origi
/* Launch a circuit to the client's chosen rendezvous point. */ - for (i=0;i<MAX_REND_FAILURES;i++) { - int max_rend_failures=get_max_rend_failures(); ++ int max_rend_failures=hs_get_service_max_rend_failures(); + for (i=0;i<max_rend_failures;i++) { int flags = CIRCLAUNCH_NEED_CAPACITY | CIRCLAUNCH_IS_INTERNAL; if (circ_needs_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; /* A Single Onion Service only uses a direct connection if its @@@ -3067,8 -2944,13 +3068,13 @@@ rend_service_relaunch_rendezvous(origin } oldcirc->hs_service_side_rend_circ_has_been_relaunched = 1;
- /* We check failure_count >= get_max_rend_failures()-1 below, and the -1 - * is because we increment the failure count for our current failure ++ /* We check failure_count >= hs_get_service_max_rend_failures()-1 below, and ++ * the -1 is because we increment the failure count for our current failure + * *after* this clause. */ - int max_rend_failures = get_max_rend_failures() - 1; ++ int max_rend_failures = hs_get_service_max_rend_failures() - 1; + if (!oldcirc->build_state || - oldcirc->build_state->failure_count > MAX_REND_FAILURES || + oldcirc->build_state->failure_count >= max_rend_failures || oldcirc->build_state->expiry_time < time(NULL)) { log_info(LD_REND, "Attempt to build circuit to %s for rendezvous has failed "
tor-commits@lists.torproject.org