hs-v3: Handle client rendezvous circuit timeout

With v3, the "pending_final_cpath" of a circuit is always NULL which means
that for v3, established client rendezvous circuit waiting for the intro point
to ACK, will always end up timing out quickly.

This can increase the delays to which you connect to a service since in order
to succeed, the rendezvous circuit needs to fully established
(CIRCUIT_PURPOSE_C_REND_JOINED) within the cutoff of the introduction circuit
as well which is these days around 2-3 seconds.

Fixes #32021

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2019-12-03 11:08:13 -05:00 committed by George Kadianakis
parent 65759f2901
commit fc32349adc
4 changed files with 61 additions and 9 deletions

7
changes/ticket32021 Normal file
View File

@ -0,0 +1,7 @@
o Minor bugfixes (onion services v3, client):
- Properly handle the client rendezvous circuit timeout. This results in
better reachability because tor doesn't timeout a rendezvous circuit
awaiting the introduction ACK and thus preventing tor to re-establish all
circuits because the rendezvous circuit timed out too early. Fixes bug
32021; bugfix on 0.3.2.1-alpha.

View File

@ -775,16 +775,11 @@ circuit_expire_building(void)
if (!(TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out)) {
switch (victim->purpose) {
case CIRCUIT_PURPOSE_C_REND_READY:
/* We only want to spare a rend circ if it has been specified in
* an INTRODUCE1 cell sent to a hidden service. A circ's
* pending_final_cpath field is non-NULL iff it is a rend circ
* and we have tried to send an INTRODUCE1 cell specifying it.
* Thus, if the pending_final_cpath field *is* NULL, then we
* want to not spare it. */
if (TO_ORIGIN_CIRCUIT(victim)->build_state &&
TO_ORIGIN_CIRCUIT(victim)->build_state->pending_final_cpath ==
NULL)
/* We only want to spare a rend circ iff it has been specified in an
* INTRODUCE1 cell sent to a hidden service. */
if (!hs_circ_is_rend_sent_in_intro1(CONST_TO_ORIGIN_CIRCUIT(victim))) {
break;
}
/* fallthrough! */
case CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT:
case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED:

View File

@ -1296,3 +1296,51 @@ hs_circ_cleanup_on_repurpose(circuit_t *circ)
hs_circuitmap_remove_circuit(circ);
}
}
/** Return true iff the given established client rendezvous circuit was sent
* into the INTRODUCE1 cell. This is called so we can take a decision on
* expiring or not the circuit.
*
* The caller MUST make sure the circuit is an established client rendezvous
* circuit (purpose: CIRCUIT_PURPOSE_C_REND_READY).
*
* This function supports all onion service versions. */
bool
hs_circ_is_rend_sent_in_intro1(const origin_circuit_t *circ)
{
tor_assert(circ);
/* This can only be called for a rendezvous circuit that is an established
* confirmed rendezsvous circuit but without an introduction ACK. */
tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_REND_READY);
/* The v2 and v3 circuit are handled differently:
*
* v2: A circ's pending_final_cpath field is non-NULL iff it is a rend circ
* and we have tried to send an INTRODUCE1 cell specifying it. Thus, if the
* pending_final_cpath field *is* NULL, then we want to not spare it.
*
* v3: When the INTRODUCE1 cell is sent, the introduction encryption public
* key is copied in the rendezvous circuit hs identifier. If it is a valid
* key, we know that this circuit is waiting the ACK on the introduction
* circuit. We want to _not_ spare the circuit if the key was never set. */
if (circ->rend_data) {
/* v2. */
if (circ->build_state && circ->build_state->pending_final_cpath != NULL) {
return true;
}
} else if (circ->hs_ident) {
/* v3. */
if (curve25519_public_key_is_ok(&circ->hs_ident->intro_enc_pk)) {
return true;
}
} else {
/* A circuit with an HS purpose without an hs_ident or rend_data in theory
* can not happen. In case, scream loudly and return false to the caller
* that the rendezvous was not sent in the INTRO1 cell. */
tor_assert_nonfatal_unreached();
}
/* The rendezvous has not been specified in the INTRODUCE1 cell. */
return false;
}

View File

@ -66,6 +66,8 @@ int hs_circuit_setup_e2e_rend_circ(origin_circuit_t *circ,
int hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
const uint8_t *rend_cell_body);
bool hs_circ_is_rend_sent_in_intro1(const origin_circuit_t *circ);
#ifdef HS_CIRCUIT_PRIVATE
STATIC hs_ident_circuit_t *