diff options
Diffstat (limited to 'src/feature/hs/hs_client.c')
-rw-r--r-- | src/feature/hs/hs_client.c | 182 |
1 files changed, 156 insertions, 26 deletions
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 68d6619cea..d9c5d8ca1d 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -646,7 +646,7 @@ send_introduce1(origin_circuit_t *intro_circ, /* Send the INTRODUCE1 cell. */ if (hs_circ_send_introduce1(intro_circ, rend_circ, ip, - desc->subcredential) < 0) { + &desc->subcredential) < 0) { if (TO_CIRCUIT(intro_circ)->marked_for_close) { /* If the introduction circuit was closed, we were unable to send the * cell for some reasons. In any case, the intro circuit has to be @@ -704,8 +704,11 @@ send_introduce1(origin_circuit_t *intro_circ, } /** Using the introduction circuit circ, setup the authentication key of the - * intro point this circuit has extended to. */ -static void + * intro point this circuit has extended to. + * + * Return 0 if everything went well, otherwise return -1 in the case of errors. + */ +static int setup_intro_circ_auth_key(origin_circuit_t *circ) { const hs_descriptor_t *desc; @@ -719,27 +722,28 @@ setup_intro_circ_auth_key(origin_circuit_t *circ) * and the client descriptor cache that gets purged (NEWNYM) or the * cleaned up because it expired. Mark the circuit for close so a new * descriptor fetch can occur. */ - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - goto end; + goto err; } /* We will go over every intro point and try to find which one is linked to * that circuit. Those lists are small so it's not that expensive. */ ip = find_desc_intro_point_by_legacy_id( circ->build_state->chosen_exit->identity_digest, desc); - if (ip) { - /* We got it, copy its authentication key to the identifier. */ - ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk, - &ip->auth_key_cert->signed_key); - goto end; + if (!ip) { + /* Reaching this point means we didn't find any intro point for this + * circuit which is not supposed to happen. */ + log_info(LD_REND,"Could not match opened intro circuit with intro point."); + goto err; } - /* Reaching this point means we didn't find any intro point for this circuit - * which is not supposed to happen. */ - tor_assert_nonfatal_unreached(); + /* We got it, copy its authentication key to the identifier. */ + ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + return 0; - end: - return; + err: + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); + return -1; } /** Called when an introduction circuit has opened. */ @@ -754,7 +758,9 @@ client_intro_circ_has_opened(origin_circuit_t *circ) /* This is an introduction circuit so we'll attach the correct * authentication key to the circuit identifier so it can be identified * properly later on. */ - setup_intro_circ_auth_key(circ); + if (setup_intro_circ_auth_key(circ) < 0) { + return; + } connection_ap_attach_pending(1); } @@ -772,10 +778,16 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ) * the v3 rendezvous protocol */ if (rp_ei) { const node_t *rp_node = node_get_by_id(rp_ei->identity_digest); - if (rp_node) { - if (BUG(!node_supports_v3_rendezvous_point(rp_node))) { - return; - } + if (rp_node && !node_supports_v3_rendezvous_point(rp_node)) { + /* Even tho we checked that this node supported v3 when we created the + rendezvous circuit, there is a chance that we might think it does + not support v3 anymore. This might happen if we got a new consensus + in the meanwhile, where the relay is still listed but its listed + descriptor digest has changed and hence we can't access its 'ri' or + 'md'. */ + log_info(LD_REND, "Rendezvous node %s did not support v3 after circuit " + "has opened.", safe_str_client(extend_info_describe(rp_ei))); + return; } } @@ -961,6 +973,87 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) return ei; } +/** Return true iff all intro points for the given service have timed out. */ +static bool +intro_points_all_timed_out(const ed25519_public_key_t *service_pk) +{ + bool ret = false; + + tor_assert(service_pk); + + const hs_descriptor_t *desc = hs_cache_lookup_as_client(service_pk); + if (BUG(!desc)) { + /* We can't introduce without a descriptor so ending up here means somehow + * between the introduction failure and this, the cache entry was removed + * which shouldn't be possible in theory. */ + goto end; + } + + SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points, + const hs_desc_intro_point_t *, ip) { + const hs_cache_intro_state_t *state = + hs_cache_client_intro_state_find(service_pk, + &ip->auth_key_cert->signed_key); + if (!state || !state->timed_out) { + /* No state or if this intro point has not timed out, we are done since + * clearly not all of them have timed out. */ + goto end; + } + } SMARTLIST_FOREACH_END(ip); + + /* Exiting the loop here means that all intro points we've looked at have + * timed out. Note that we can _not_ have a descriptor without intro points + * in the client cache. */ + ret = true; + + end: + return ret; +} + +/** Called when a rendezvous circuit has timed out. Every stream attached to + * the circuit will get set with the SOCKS5_HS_REND_FAILED (0xF3) extended + * error code so if the connection to the rendezvous point ends up not + * working, this code could be sent back as a reason. */ +static void +socks_mark_rend_circuit_timed_out(const origin_circuit_t *rend_circ) +{ + tor_assert(rend_circ); + + /* For each entry connection attached to this rendezvous circuit, report + * the error. */ + for (edge_connection_t *edge = rend_circ->p_streams; edge; + edge = edge->next_stream) { + entry_connection_t *entry = EDGE_TO_ENTRY_CONN(edge); + if (entry->socks_request) { + entry->socks_request->socks_extended_error_code = + SOCKS5_HS_REND_FAILED; + } + } +} + +/** Called when introduction has failed meaning there is no more usable + * introduction points to be used (either NACKed or failed) for the given + * entry connection. + * + * This function only reports back the SOCKS5_HS_INTRO_FAILED (0xF2) code or + * SOCKS5_HS_INTRO_TIMEDOUT (0xF7) if all intros have timed out. The caller + * has to make sure to close the entry connections. */ +static void +socks_mark_introduction_failed(entry_connection_t *conn, + const ed25519_public_key_t *identity_pk) +{ + socks5_reply_status_t code = SOCKS5_HS_INTRO_FAILED; + + tor_assert(conn); + tor_assert(conn->socks_request); + tor_assert(identity_pk); + + if (intro_points_all_timed_out(identity_pk)) { + code = SOCKS5_HS_INTRO_TIMEDOUT; + } + conn->socks_request->socks_extended_error_code = code; +} + /** For this introduction circuit, we'll look at if we have any usable * introduction point left for this service. If so, we'll use the circuit to * re-extend to a new intro point. Else, we'll close the circuit and its @@ -978,8 +1071,10 @@ close_or_reextend_intro_circ(origin_circuit_t *intro_circ) tor_assert(intro_circ); desc = hs_cache_lookup_as_client(&intro_circ->hs_ident->identity_pk); - if (BUG(desc == NULL)) { - /* We can't continue without a descriptor. */ + if (desc == NULL) { + /* We can't continue without a descriptor. This is possible if the cache + * was cleaned up between the intro point established and the reception of + * the introduce ack. */ goto close; } /* We still have the descriptor, great! Let's try to see if we can @@ -1313,6 +1408,10 @@ client_desc_has_arrived(const smartlist_t *entry_conns) if (!hs_client_any_intro_points_usable(identity_pk, desc)) { log_info(LD_REND, "Hidden service descriptor is unusable. " "Closing streams."); + /* Report the extended socks error code that we were unable to introduce + * to the service. */ + socks_mark_introduction_failed(entry_conn, identity_pk); + connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_RESOLVEFAILED); /* We are unable to use the descriptor so remove the directory request @@ -1762,6 +1861,37 @@ get_hs_client_auths_map(void) /* ========== */ /** Called when a circuit was just cleaned up. This is done right before the + * circuit is marked for close. */ +void +hs_client_circuit_cleanup_on_close(const circuit_t *circ) +{ + bool has_timed_out; + + tor_assert(circ); + tor_assert(CIRCUIT_IS_ORIGIN(circ)); + + has_timed_out = + (circ->marked_for_close_orig_reason == END_CIRC_REASON_TIMEOUT); + + switch (circ->purpose) { + case CIRCUIT_PURPOSE_C_ESTABLISH_REND: + case CIRCUIT_PURPOSE_C_REND_READY: + case CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: + case CIRCUIT_PURPOSE_C_REND_JOINED: + /* Report extended SOCKS error code when a rendezvous circuit times out. + * This MUST be done on_close() because it is possible the entry + * connection would get closed before the circuit is freed and thus + * would fail to report the error code. */ + if (has_timed_out) { + socks_mark_rend_circuit_timed_out(CONST_TO_ORIGIN_CIRCUIT(circ)); + } + break; + default: + break; + } +} + +/** Called when a circuit was just cleaned up. This is done right before the * circuit is freed. */ void hs_client_circuit_cleanup_on_free(const circuit_t *circ) @@ -1845,7 +1975,7 @@ hs_client_decode_descriptor(const char *desc_str, hs_descriptor_t **desc) { hs_desc_decode_status_t ret; - uint8_t subcredential[DIGEST256_LEN]; + hs_subcredential_t subcredential; ed25519_public_key_t blinded_pubkey; hs_client_service_authorization_t *client_auth = NULL; curve25519_secret_key_t *client_auth_sk = NULL; @@ -1865,13 +1995,13 @@ hs_client_decode_descriptor(const char *desc_str, uint64_t current_time_period = hs_get_time_period_num(0); hs_build_blinded_pubkey(service_identity_pk, NULL, 0, current_time_period, &blinded_pubkey); - hs_get_subcredential(service_identity_pk, &blinded_pubkey, subcredential); + hs_get_subcredential(service_identity_pk, &blinded_pubkey, &subcredential); } /* Parse descriptor */ - ret = hs_desc_decode_descriptor(desc_str, subcredential, + ret = hs_desc_decode_descriptor(desc_str, &subcredential, client_auth_sk, desc); - memwipe(subcredential, 0, sizeof(subcredential)); + memwipe(&subcredential, 0, sizeof(subcredential)); if (ret != HS_DESC_DECODE_OK) { goto err; } |