diff --git a/changes/ticket25417 b/changes/ticket25417 new file mode 100644 index 0000000000..41f2acc988 --- /dev/null +++ b/changes/ticket25417 @@ -0,0 +1,4 @@ + o Minor features (controller): + - Add onion service version 3 support to HSFETCH. Previously, only + version 2 onion services were supported. Closes ticket 25417. + Patch by Neel Chauhan diff --git a/src/feature/control/control.c b/src/feature/control/control.c index 170037e060..d7299a4cf6 100644 --- a/src/feature/control/control.c +++ b/src/feature/control/control.c @@ -4426,6 +4426,8 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, static const char *v2_str = "v2-"; const size_t v2_str_len = strlen(v2_str); rend_data_t *rend_query = NULL; + ed25519_public_key_t v3_pk; + uint32_t version; /* Make sure we have at least one argument, the HSAddress. */ args = getargs_helper(hsfetch_command, conn, body, 1, -1); @@ -4438,6 +4440,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, /* Test if it's an HS address without the .onion part. */ if (rend_valid_v2_service_id(arg1)) { hsaddress = arg1; + version = HS_VERSION_TWO; } else if (strcmpstart(arg1, v2_str) == 0 && rend_valid_descriptor_id(arg1 + v2_str_len) && base32_decode(digest, sizeof(digest), arg1 + v2_str_len, @@ -4445,6 +4448,11 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, /* We have a well formed version 2 descriptor ID. Keep the decoded value * of the id. */ desc_id = digest; + version = HS_VERSION_TWO; + } else if (hs_address_is_valid(arg1)) { + hsaddress = arg1; + version = HS_VERSION_THREE; + hs_parse_address(hsaddress, &v3_pk, NULL, NULL); } else { connection_printf_to_buf(conn, "513 Invalid argument \"%s\"\r\n", arg1); @@ -4481,11 +4489,13 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, } } - rend_query = rend_data_client_create(hsaddress, desc_id, NULL, - REND_NO_AUTH); - if (rend_query == NULL) { - connection_printf_to_buf(conn, "551 Error creating the HS query\r\n"); - goto done; + if (version == HS_VERSION_TWO) { + rend_query = rend_data_client_create(hsaddress, desc_id, NULL, + REND_NO_AUTH); + if (rend_query == NULL) { + connection_printf_to_buf(conn, "551 Error creating the HS query\r\n"); + goto done; + } } /* Using a descriptor ID, we force the user to provide at least one @@ -4504,7 +4514,11 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, /* Trigger the fetch using the built rend query and possibly a list of HS * directory to use. This function ignores the client cache thus this will * always send a fetch command. */ - rend_client_fetch_v2_desc(rend_query, hsdirs); + if (version == HS_VERSION_TWO) { + rend_client_fetch_v2_desc(rend_query, hsdirs); + } else if (version == HS_VERSION_THREE) { + hs_control_hsfetch_command(&v3_pk, hsdirs); + } done: SMARTLIST_FOREACH(args, char *, cp, tor_free(cp)); diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 5fded92fe3..7dc856ae54 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -459,6 +459,24 @@ fetch_v3_desc, (const ed25519_public_key_t *onion_identity_pk)) return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs); } +/* With a given onion_identity_pk, fetch its descriptor. If + * hsdirs is specified, use the directory servers specified in the list. + * Else, use a random server. */ +void +hs_client_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk, + const smartlist_t *hsdirs) +{ + tor_assert(onion_identity_pk); + + if (hsdirs != NULL) { + SMARTLIST_FOREACH_BEGIN(hsdirs, const routerstatus_t *, hsdir) { + directory_launch_v3_desc_fetch(onion_identity_pk, hsdir); + } SMARTLIST_FOREACH_END(hsdir); + } else { + fetch_v3_desc(onion_identity_pk); + } +} + /* Make sure that the given v3 origin circuit circ is a valid correct * introduction circuit. This will BUG() on any problems and hard assert if * the anonymity of the circuit is not ok. Return 0 on success else -1 where diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h index f6fb167ea2..47c525242f 100644 --- a/src/feature/hs/hs_client.h +++ b/src/feature/hs/hs_client.h @@ -44,6 +44,10 @@ typedef struct hs_client_service_authorization_t { void hs_client_note_connection_attempt_succeeded( const edge_connection_t *conn); +void hs_client_launch_v3_desc_fetch( + const ed25519_public_key_t *onion_identity_pk, + const smartlist_t *hsdirs); + int hs_client_decode_descriptor( const char *desc_str, const ed25519_public_key_t *service_identity_pk, diff --git a/src/feature/hs/hs_control.c b/src/feature/hs/hs_control.c index a21788ecd7..df8c1958b5 100644 --- a/src/feature/hs/hs_control.c +++ b/src/feature/hs/hs_control.c @@ -10,6 +10,7 @@ #include "feature/control/control.h" #include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_util.h" +#include "feature/hs/hs_client.h" #include "feature/hs/hs_common.h" #include "feature/hs/hs_control.h" #include "feature/hs/hs_descriptor.h" @@ -259,3 +260,16 @@ hs_control_hspost_command(const char *body, const char *onion_address, smartlist_free(hsdirs); return ret; } + +/* With a given onion_identity_pk, fetch its descriptor, optionally + * using the list of directory servers given in hsdirs, or a random + * server if it is NULL. This function calls hs_client_launch_v3_desc_fetch(). + */ +void +hs_control_hsfetch_command(const ed25519_public_key_t *onion_identity_pk, + const smartlist_t *hsdirs) +{ + tor_assert(onion_identity_pk); + + hs_client_launch_v3_desc_fetch(onion_identity_pk, hsdirs); +} diff --git a/src/feature/hs/hs_control.h b/src/feature/hs/hs_control.h index 63e3fe13d6..2f5dcd2154 100644 --- a/src/feature/hs/hs_control.h +++ b/src/feature/hs/hs_control.h @@ -48,5 +48,9 @@ void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, int hs_control_hspost_command(const char *body, const char *onion_address, const smartlist_t *hsdirs_rs); +/* Command "HSFETCH [...]" */ +void hs_control_hsfetch_command(const ed25519_public_key_t *onion_identity_pk, + const smartlist_t *hsdirs); + #endif /* !defined(TOR_HS_CONTROL_H) */