mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 23:53:32 +01:00
Merge branch 'tor-github/pr/1857'
This commit is contained in:
commit
7a82c972ef
3
changes/ticket32542
Normal file
3
changes/ticket32542
Normal file
@ -0,0 +1,3 @@
|
||||
o Minor feature (onion service client, SOCKS5):
|
||||
- Add 3 new SocksPort ExtendedErrors (F2, F3, F7) that reports back new type
|
||||
of onion service connection failures. Closes ticket 32542.
|
@ -1563,15 +1563,13 @@ The following options are useful only for clients (that is, if
|
||||
|
||||
X'F2' Onion Service Introduction Failed
|
||||
|
||||
Client failed to introduce to the service meaning the descriptor
|
||||
was found but the service is not connected anymore to the
|
||||
introduction point. The service has likely changed its descriptor
|
||||
or is not running. (v3 only)
|
||||
All introduction attempts failed either due to a combination of
|
||||
NACK by the intro point or time out. (v3 only)
|
||||
|
||||
X'F3' Onion Service Rendezvous Failed
|
||||
|
||||
Client failed to rendezvous with the service which means that the
|
||||
client is unable to finalize the connection. (v3 only)
|
||||
Every rendezvous circuit has timed out and thus the client is
|
||||
unable to rendezvous with the service. (v3 only)
|
||||
|
||||
X'F4' Onion Service Missing Client Authorization
|
||||
|
||||
@ -1592,6 +1590,11 @@ The following options are useful only for clients (that is, if
|
||||
error is returned: address checksum doesn't match, ed25519 public
|
||||
key is invalid or the encoding is invalid. (v3 only)
|
||||
|
||||
X'F7' Onion Service Introduction Timed Out
|
||||
|
||||
Similar to X'F2' code but in this case, all introduction attemps
|
||||
have failed due to a time out. (v3 only)
|
||||
|
||||
// Anchor only for formatting, not visible in the man page.
|
||||
[[SocksPortFlagsMisc]]::
|
||||
Flags are processed left to right. If flags conflict, the last flag on the
|
||||
|
@ -621,6 +621,20 @@ setup_introduce1_data(const hs_desc_intro_point_t *ip,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Helper: cleanup function for client circuit. This is for every HS version.
|
||||
* It is called from hs_circ_cleanup_on_close() entry point. */
|
||||
static void
|
||||
cleanup_on_close_client_circ(circuit_t *circ)
|
||||
{
|
||||
tor_assert(circ);
|
||||
|
||||
if (circuit_is_hs_v3(circ)) {
|
||||
hs_client_circuit_cleanup_on_close(circ);
|
||||
}
|
||||
/* It is possible the circuit has an HS purpose but no identifier (rend_data
|
||||
* or hs_ident). Thus possible that this passess through. */
|
||||
}
|
||||
|
||||
/** Helper: cleanup function for client circuit. This is for every HS version.
|
||||
* It is called from hs_circ_cleanup_on_free() entry point. */
|
||||
static void
|
||||
@ -1293,6 +1307,10 @@ hs_circ_cleanup_on_close(circuit_t *circ)
|
||||
{
|
||||
tor_assert(circ);
|
||||
|
||||
if (circuit_purpose_is_hs_client(circ->purpose)) {
|
||||
cleanup_on_close_client_circ(circ);
|
||||
}
|
||||
|
||||
/* On close, we simply remove it from the circuit map. It can not be used
|
||||
* anymore. We keep this code path fast and lean. */
|
||||
|
||||
|
@ -961,6 +961,81 @@ 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);
|
||||
|
||||
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 streams 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_report_rend_circuit_timed_out(const origin_circuit_t *rend_circ)
|
||||
{
|
||||
tor_assert(rend_circ);
|
||||
|
||||
/* For each entry connections 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_report_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
|
||||
@ -1313,6 +1388,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_report_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
|
||||
@ -1761,6 +1840,37 @@ get_hs_client_auths_map(void)
|
||||
/* Public API */
|
||||
/* ========== */
|
||||
|
||||
/** 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 timeouts.
|
||||
* This MUST be done on_close() because it is possible the entry
|
||||
* connection would get closed before the circuit is freed and thus
|
||||
* failing to report the error code. */
|
||||
if (has_timed_out) {
|
||||
socks_report_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
|
||||
|
@ -110,6 +110,7 @@ int hs_client_send_introduce1(origin_circuit_t *intro_circ,
|
||||
origin_circuit_t *rend_circ);
|
||||
|
||||
void hs_client_circuit_has_opened(origin_circuit_t *circ);
|
||||
void hs_client_circuit_cleanup_on_close(const circuit_t *circ);
|
||||
void hs_client_circuit_cleanup_on_free(const circuit_t *circ);
|
||||
|
||||
int hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
|
||||
|
@ -37,6 +37,7 @@ typedef enum {
|
||||
SOCKS5_HS_MISSING_CLIENT_AUTH = 0xF4,
|
||||
SOCKS5_HS_BAD_CLIENT_AUTH = 0xF5,
|
||||
SOCKS5_HS_BAD_ADDRESS = 0xF6,
|
||||
SOCKS5_HS_INTRO_TIMEDOUT = 0xF7,
|
||||
} socks5_reply_status_t;
|
||||
|
||||
#endif /* !defined(TOR_SOCKS5_STATUS_H) */
|
||||
|
@ -1189,7 +1189,11 @@ static void
|
||||
test_socks_hs_errors(void *arg)
|
||||
{
|
||||
int ret;
|
||||
char digest[DIGEST_LEN];
|
||||
char *desc_encoded = NULL;
|
||||
circuit_t *circ = NULL;
|
||||
origin_circuit_t *ocirc = NULL;
|
||||
tor_addr_t addr;
|
||||
ed25519_keypair_t service_kp;
|
||||
ed25519_keypair_t signing_kp;
|
||||
entry_connection_t *socks_conn = NULL;
|
||||
@ -1236,6 +1240,73 @@ test_socks_hs_errors(void *arg)
|
||||
desc = hs_helper_build_hs_desc_with_ip(&service_kp);
|
||||
tt_assert(desc);
|
||||
|
||||
/* Before testing the client authentication error code, encode the
|
||||
* descriptor with no client auth. */
|
||||
ret = hs_desc_encode_descriptor(desc, &service_kp, NULL, &desc_encoded);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_assert(desc_encoded);
|
||||
|
||||
/*
|
||||
* Test the introduction failure codes (X'F2' and X'F7')
|
||||
*/
|
||||
|
||||
/* First, we have to put all the IPs in the failure cache. */
|
||||
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
|
||||
hs_desc_intro_point_t *, ip) {
|
||||
hs_cache_client_intro_state_note(&service_kp.pubkey,
|
||||
&ip->auth_key_cert->signed_key,
|
||||
INTRO_POINT_FAILURE_GENERIC);
|
||||
} SMARTLIST_FOREACH_END(ip);
|
||||
|
||||
hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
|
||||
tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
|
||||
SOCKS5_HS_INTRO_FAILED);
|
||||
|
||||
/* Purge client cache of the descriptor so we can go again. */
|
||||
hs_cache_purge_as_client();
|
||||
|
||||
/* Second, set all failures to be time outs. */
|
||||
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
|
||||
hs_desc_intro_point_t *, ip) {
|
||||
hs_cache_client_intro_state_note(&service_kp.pubkey,
|
||||
&ip->auth_key_cert->signed_key,
|
||||
INTRO_POINT_FAILURE_TIMEOUT);
|
||||
} SMARTLIST_FOREACH_END(ip);
|
||||
|
||||
hs_client_dir_fetch_done(dir_conn, "Reason", desc_encoded, 200);
|
||||
tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
|
||||
SOCKS5_HS_INTRO_TIMEDOUT);
|
||||
|
||||
/* Purge client cache of the descriptor so we can go again. */
|
||||
hs_cache_purge_as_client();
|
||||
|
||||
/*
|
||||
* Test the rendezvous failure codes (X'F3')
|
||||
*/
|
||||
|
||||
circ = dummy_origin_circuit_new(0);
|
||||
tt_assert(circ);
|
||||
circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
|
||||
ocirc = TO_ORIGIN_CIRCUIT(circ);
|
||||
ocirc->hs_ident = hs_ident_circuit_new(&service_kp.pubkey);
|
||||
ocirc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t));
|
||||
/* Code path will log this exit so build it. */
|
||||
ocirc->build_state->chosen_exit = extend_info_new("TestNickname", digest,
|
||||
NULL, NULL, NULL, &addr,
|
||||
4242);
|
||||
/* Attach socks connection to this rendezvous circuit. */
|
||||
ocirc->p_streams = ENTRY_TO_EDGE_CONN(socks_conn);
|
||||
/* Trigger the rendezvous failure. Timeout the circuit and free. */
|
||||
circuit_mark_for_close(circ, END_CIRC_REASON_TIMEOUT);
|
||||
|
||||
tt_int_op(socks_conn->socks_request->socks_extended_error_code, OP_EQ,
|
||||
SOCKS5_HS_REND_FAILED);
|
||||
|
||||
/*
|
||||
* Test client authorization codes.
|
||||
*/
|
||||
|
||||
tor_free(desc_encoded);
|
||||
crypto_rand((char *) descriptor_cookie, sizeof(descriptor_cookie));
|
||||
ret = hs_desc_encode_descriptor(desc, &service_kp, descriptor_cookie,
|
||||
&desc_encoded);
|
||||
@ -1277,6 +1348,7 @@ test_socks_hs_errors(void *arg)
|
||||
connection_free_minimal(TO_CONN(dir_conn));
|
||||
hs_descriptor_free(desc);
|
||||
tor_free(desc_encoded);
|
||||
circuit_free(circ);
|
||||
|
||||
hs_free_all();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user