123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- /* Copyright (c) 2016-2019, The Tor Project, Inc. */
- /* See LICENSE for licensing information */
- /**
- * \file hs_circuitmap.c
- *
- * \brief Hidden service circuitmap: A hash table that maps binary tokens to
- * introduction and rendezvous circuits; it's used:
- * (a) by relays acting as intro points and rendezvous points
- * (b) by hidden services to find intro and rend circuits and
- * (c) by HS clients to find rendezvous circuits.
- **/
- #define HS_CIRCUITMAP_PRIVATE
- #include "core/or/or.h"
- #include "app/config/config.h"
- #include "core/or/circuitlist.h"
- #include "feature/hs/hs_circuitmap.h"
- #include "core/or/or_circuit_st.h"
- #include "core/or/origin_circuit_st.h"
- /************************** HS circuitmap code *******************************/
- /* This is the hidden service circuitmap. It's a hash table that maps
- introduction and rendezvous tokens to specific circuits such that given a
- token it's easy to find the corresponding circuit. */
- static struct hs_circuitmap_ht *the_hs_circuitmap = NULL;
- /* This is a helper function used by the hash table code (HT_). It returns 1 if
- * two circuits have the same HS token. */
- static int
- hs_circuits_have_same_token(const circuit_t *first_circuit,
- const circuit_t *second_circuit)
- {
- const hs_token_t *first_token;
- const hs_token_t *second_token;
- tor_assert(first_circuit);
- tor_assert(second_circuit);
- first_token = first_circuit->hs_token;
- second_token = second_circuit->hs_token;
- /* Both circs must have a token */
- if (BUG(!first_token) || BUG(!second_token)) {
- return 0;
- }
- if (first_token->type != second_token->type) {
- return 0;
- }
- if (first_token->token_len != second_token->token_len)
- return 0;
- return tor_memeq(first_token->token,
- second_token->token,
- first_token->token_len);
- }
- /* This is a helper function for the hash table code (HT_). It hashes a circuit
- * HS token into an unsigned int for use as a key by the hash table routines.*/
- static inline unsigned int
- hs_circuit_hash_token(const circuit_t *circuit)
- {
- tor_assert(circuit->hs_token);
- return (unsigned) siphash24g(circuit->hs_token->token,
- circuit->hs_token->token_len);
- }
- /* Register the circuitmap hash table */
- HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct
- circuit_t, // The name of the element struct,
- hs_circuitmap_node, // The name of HT_ENTRY member
- hs_circuit_hash_token, hs_circuits_have_same_token)
- HT_GENERATE2(hs_circuitmap_ht, circuit_t, hs_circuitmap_node,
- hs_circuit_hash_token, hs_circuits_have_same_token,
- 0.6, tor_reallocarray, tor_free_)
- #ifdef TOR_UNIT_TESTS
- /* Return the global HS circuitmap. Used by unittests. */
- hs_circuitmap_ht *
- get_hs_circuitmap(void)
- {
- return the_hs_circuitmap;
- }
- #endif /* defined(TOR_UNIT_TESTS) */
- /****************** HS circuitmap utility functions **************************/
- /** Return a new HS token of type <b>type</b> containing <b>token</b>. */
- static hs_token_t *
- hs_token_new(hs_token_type_t type, size_t token_len,
- const uint8_t *token)
- {
- tor_assert(token);
- hs_token_t *hs_token = tor_malloc_zero(sizeof(hs_token_t));
- hs_token->type = type;
- hs_token->token_len = token_len;
- hs_token->token = tor_memdup(token, token_len);
- return hs_token;
- }
- #define hs_token_free(val) \
- FREE_AND_NULL(hs_token_t, hs_token_free_, (val))
- /** Free memory allocated by this <b>hs_token</b>. */
- static void
- hs_token_free_(hs_token_t *hs_token)
- {
- if (!hs_token) {
- return;
- }
- tor_free(hs_token->token);
- tor_free(hs_token);
- }
- /** Return the circuit from the circuitmap with token <b>search_token</b>. */
- static circuit_t *
- get_circuit_with_token(hs_token_t *search_token)
- {
- tor_assert(the_hs_circuitmap);
- /* We use a dummy circuit object for the hash table search routine. */
- circuit_t search_circ;
- search_circ.hs_token = search_token;
- return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ);
- }
- /* Helper function that registers <b>circ</b> with <b>token</b> on the HS
- circuitmap. This function steals reference of <b>token</b>. */
- static void
- hs_circuitmap_register_impl(circuit_t *circ, hs_token_t *token)
- {
- tor_assert(circ);
- tor_assert(token);
- tor_assert(the_hs_circuitmap);
- /* If this circuit already has a token, clear it. */
- if (circ->hs_token) {
- hs_circuitmap_remove_circuit(circ);
- }
- /* Kill old circuits with the same token. We want new intro/rend circuits to
- take precedence over old ones, so that HSes and clients and reestablish
- killed circuits without changing the HS token. */
- {
- circuit_t *found_circ;
- found_circ = get_circuit_with_token(token);
- if (found_circ) {
- hs_circuitmap_remove_circuit(found_circ);
- if (!found_circ->marked_for_close) {
- circuit_mark_for_close(found_circ, END_CIRC_REASON_FINISHED);
- }
- }
- }
- /* Register circuit and token to circuitmap. */
- circ->hs_token = token;
- HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ);
- }
- /** Helper function: Register <b>circ</b> of <b>type</b> on the HS
- * circuitmap. Use the HS <b>token</b> as the key to the hash table. If
- * <b>token</b> is not set, clear the circuit of any HS tokens. */
- static void
- hs_circuitmap_register_circuit(circuit_t *circ,
- hs_token_type_t type, size_t token_len,
- const uint8_t *token)
- {
- hs_token_t *hs_token = NULL;
- /* Create a new token and register it to the circuitmap */
- tor_assert(token);
- hs_token = hs_token_new(type, token_len, token);
- tor_assert(hs_token);
- hs_circuitmap_register_impl(circ, hs_token);
- }
- /* Helper function for hs_circuitmap_get_origin_circuit() and
- * hs_circuitmap_get_or_circuit(). Because only circuit_t are indexed in the
- * circuitmap, this function returns object type so the specialized functions
- * using this helper can upcast it to the right type.
- *
- * Return NULL if not such circuit is found. */
- static circuit_t *
- hs_circuitmap_get_circuit_impl(hs_token_type_t type,
- size_t token_len,
- const uint8_t *token,
- uint8_t wanted_circ_purpose)
- {
- circuit_t *found_circ = NULL;
- tor_assert(the_hs_circuitmap);
- /* Check the circuitmap if we have a circuit with this token */
- {
- hs_token_t *search_hs_token = hs_token_new(type, token_len, token);
- tor_assert(search_hs_token);
- found_circ = get_circuit_with_token(search_hs_token);
- hs_token_free(search_hs_token);
- }
- /* Check that the circuit is useful to us */
- if (!found_circ ||
- found_circ->purpose != wanted_circ_purpose ||
- found_circ->marked_for_close) {
- return NULL;
- }
- return found_circ;
- }
- /* Helper function: Query circuitmap for origin circuit with <b>token</b> of
- * size <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose
- * equal to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked
- * for close. Return NULL if no such circuit is found. */
- static origin_circuit_t *
- hs_circuitmap_get_origin_circuit(hs_token_type_t type,
- size_t token_len,
- const uint8_t *token,
- uint8_t wanted_circ_purpose)
- {
- circuit_t *circ;
- tor_assert(token);
- tor_assert(CIRCUIT_PURPOSE_IS_ORIGIN(wanted_circ_purpose));
- circ = hs_circuitmap_get_circuit_impl(type, token_len, token,
- wanted_circ_purpose);
- if (!circ) {
- return NULL;
- }
- tor_assert(CIRCUIT_IS_ORIGIN(circ));
- return TO_ORIGIN_CIRCUIT(circ);
- }
- /* Helper function: Query circuitmap for OR circuit with <b>token</b> of size
- * <b>token_len</b> and <b>type</b>. Only returns a circuit with purpose equal
- * to the <b>wanted_circ_purpose</b> parameter and if it is NOT marked for
- * close. Return NULL if no such circuit is found. */
- static or_circuit_t *
- hs_circuitmap_get_or_circuit(hs_token_type_t type,
- size_t token_len,
- const uint8_t *token,
- uint8_t wanted_circ_purpose)
- {
- circuit_t *circ;
- tor_assert(token);
- tor_assert(!CIRCUIT_PURPOSE_IS_ORIGIN(wanted_circ_purpose));
- circ = hs_circuitmap_get_circuit_impl(type, token_len, token,
- wanted_circ_purpose);
- if (!circ) {
- return NULL;
- }
- tor_assert(CIRCUIT_IS_ORCIRC(circ));
- return TO_OR_CIRCUIT(circ);
- }
- /************** Public circuitmap API ****************************************/
- /**** Public relay-side getters: */
- /* Public function: Return v2 and v3 introduction circuit to this relay.
- * Always return a newly allocated list for which it is the caller's
- * responsability to free it. */
- smartlist_t *
- hs_circuitmap_get_all_intro_circ_relay_side(void)
- {
- circuit_t **iter;
- smartlist_t *circuit_list = smartlist_new();
- HT_FOREACH(iter, hs_circuitmap_ht, the_hs_circuitmap) {
- circuit_t *circ = *iter;
- /* An origin circuit or purpose is wrong or the hs token is not set to be
- * a v2 or v3 intro relay side type, we ignore the circuit. Else, we have
- * a match so add it to our list. */
- if (CIRCUIT_IS_ORIGIN(circ) ||
- circ->purpose != CIRCUIT_PURPOSE_INTRO_POINT ||
- (circ->hs_token->type != HS_TOKEN_INTRO_V3_RELAY_SIDE &&
- circ->hs_token->type != HS_TOKEN_INTRO_V2_RELAY_SIDE)) {
- continue;
- }
- smartlist_add(circuit_list, circ);
- }
- return circuit_list;
- }
- /* Public function: Return a v3 introduction circuit to this relay with
- * <b>auth_key</b>. Return NULL if no such circuit is found in the
- * circuitmap. */
- or_circuit_t *
- hs_circuitmap_get_intro_circ_v3_relay_side(
- const ed25519_public_key_t *auth_key)
- {
- return hs_circuitmap_get_or_circuit(HS_TOKEN_INTRO_V3_RELAY_SIDE,
- ED25519_PUBKEY_LEN, auth_key->pubkey,
- CIRCUIT_PURPOSE_INTRO_POINT);
- }
- /* Public function: Return v2 introduction circuit to this relay with
- * <b>digest</b>. Return NULL if no such circuit is found in the circuitmap. */
- or_circuit_t *
- hs_circuitmap_get_intro_circ_v2_relay_side(const uint8_t *digest)
- {
- return hs_circuitmap_get_or_circuit(HS_TOKEN_INTRO_V2_RELAY_SIDE,
- REND_TOKEN_LEN, digest,
- CIRCUIT_PURPOSE_INTRO_POINT);
- }
- /* Public function: Return rendezvous circuit to this relay with rendezvous
- * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */
- or_circuit_t *
- hs_circuitmap_get_rend_circ_relay_side(const uint8_t *cookie)
- {
- return hs_circuitmap_get_or_circuit(HS_TOKEN_REND_RELAY_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_REND_POINT_WAITING);
- }
- /** Public relay-side setters: */
- /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_rend_circ_relay_side(or_circuit_t *circ,
- const uint8_t *cookie)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_REND_RELAY_SIDE,
- REND_TOKEN_LEN, cookie);
- }
- /* Public function: Register v2 intro circuit with key <b>digest</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_intro_circ_v2_relay_side(or_circuit_t *circ,
- const uint8_t *digest)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_INTRO_V2_RELAY_SIDE,
- REND_TOKEN_LEN, digest);
- }
- /* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_intro_circ_v3_relay_side(or_circuit_t *circ,
- const ed25519_public_key_t *auth_key)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_INTRO_V3_RELAY_SIDE,
- ED25519_PUBKEY_LEN, auth_key->pubkey);
- }
- /**** Public servide-side getters: */
- /* Public function: Return v3 introduction circuit with <b>auth_key</b>
- * originating from this hidden service. Return NULL if no such circuit is
- * found in the circuitmap. */
- origin_circuit_t *
- hs_circuitmap_get_intro_circ_v3_service_side(const
- ed25519_public_key_t *auth_key)
- {
- origin_circuit_t *circ = NULL;
- /* Check first for established intro circuits */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V3_SERVICE_SIDE,
- ED25519_PUBKEY_LEN, auth_key->pubkey,
- CIRCUIT_PURPOSE_S_INTRO);
- if (circ) {
- return circ;
- }
- /* ...if nothing found, check for pending intro circs */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V3_SERVICE_SIDE,
- ED25519_PUBKEY_LEN, auth_key->pubkey,
- CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
- return circ;
- }
- /* Public function: Return v2 introduction circuit originating from this hidden
- * service with <b>digest</b>. Return NULL if no such circuit is found in the
- * circuitmap. */
- origin_circuit_t *
- hs_circuitmap_get_intro_circ_v2_service_side(const uint8_t *digest)
- {
- origin_circuit_t *circ = NULL;
- /* Check first for established intro circuits */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V2_SERVICE_SIDE,
- REND_TOKEN_LEN, digest,
- CIRCUIT_PURPOSE_S_INTRO);
- if (circ) {
- return circ;
- }
- /* ...if nothing found, check for pending intro circs */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_INTRO_V2_SERVICE_SIDE,
- REND_TOKEN_LEN, digest,
- CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
- return circ;
- }
- /* Public function: Return rendezvous circuit originating from this hidden
- * service with rendezvous <b>cookie</b>. Return NULL if no such circuit is
- * found in the circuitmap. */
- origin_circuit_t *
- hs_circuitmap_get_rend_circ_service_side(const uint8_t *cookie)
- {
- origin_circuit_t *circ = NULL;
- /* Try to check if we have a connecting circuit. */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_SERVICE_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_S_CONNECT_REND);
- if (circ) {
- return circ;
- }
- /* Then try for connected circuit. */
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_SERVICE_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_S_REND_JOINED);
- return circ;
- }
- /* Public function: Return client-side rendezvous circuit with rendezvous
- * <b>cookie</b>. It will look for circuits with the following purposes:
- * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
- * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
- * INTRODUCE_ACK from intro point.
- *
- * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
- * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
- *
- * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
- * RENDEZVOUS2 from service.
- *
- * d) CIRCUIT_PURPOSE_C_ESTABLISH_REND: Rend circuit open but not yet
- * established.
- *
- * Return NULL if no such circuit is found in the circuitmap. */
- origin_circuit_t *
- hs_circuitmap_get_rend_circ_client_side(const uint8_t *cookie)
- {
- origin_circuit_t *circ = NULL;
- circ = hs_circuitmap_get_established_rend_circ_client_side(cookie);
- if (circ) {
- return circ;
- }
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_ESTABLISH_REND);
- return circ;
- }
- /* Public function: Return client-side established rendezvous circuit with
- * rendezvous <b>cookie</b>. It will look for circuits with the following
- * purposes:
- *
- * a) CIRCUIT_PURPOSE_C_REND_READY: Established rend circuit (received
- * RENDEZVOUS_ESTABLISHED). Waiting for RENDEZVOUS2 from service, and for
- * INTRODUCE_ACK from intro point.
- *
- * b) CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED: Established rend circuit and
- * introduce circuit acked. Waiting for RENDEZVOUS2 from service.
- *
- * c) CIRCUIT_PURPOSE_C_REND_JOINED: Established rend circuit and received
- * RENDEZVOUS2 from service.
- *
- * Return NULL if no such circuit is found in the circuitmap. */
- origin_circuit_t *
- hs_circuitmap_get_established_rend_circ_client_side(const uint8_t *cookie)
- {
- origin_circuit_t *circ = NULL;
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_READY);
- if (circ) {
- return circ;
- }
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED);
- if (circ) {
- return circ;
- }
- circ = hs_circuitmap_get_origin_circuit(HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie,
- CIRCUIT_PURPOSE_C_REND_JOINED);
- return circ;
- }
- /**** Public servide-side setters: */
- /* Public function: Register v2 intro circuit with key <b>digest</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_intro_circ_v2_service_side(origin_circuit_t *circ,
- const uint8_t *digest)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_INTRO_V2_SERVICE_SIDE,
- REND_TOKEN_LEN, digest);
- }
- /* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_intro_circ_v3_service_side(origin_circuit_t *circ,
- const ed25519_public_key_t *auth_key)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_INTRO_V3_SERVICE_SIDE,
- ED25519_PUBKEY_LEN, auth_key->pubkey);
- }
- /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
- * circuitmap. */
- void
- hs_circuitmap_register_rend_circ_service_side(origin_circuit_t *circ,
- const uint8_t *cookie)
- {
- hs_circuitmap_register_circuit(TO_CIRCUIT(circ),
- HS_TOKEN_REND_SERVICE_SIDE,
- REND_TOKEN_LEN, cookie);
- }
- /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
- * client-side circuitmap. */
- void
- hs_circuitmap_register_rend_circ_client_side(origin_circuit_t *or_circ,
- const uint8_t *cookie)
- {
- circuit_t *circ = TO_CIRCUIT(or_circ);
- { /* Basic circ purpose sanity checking */
- tor_assert_nonfatal(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
- }
- hs_circuitmap_register_circuit(circ, HS_TOKEN_REND_CLIENT_SIDE,
- REND_TOKEN_LEN, cookie);
- }
- /**** Misc public functions: */
- /** Public function: Remove this circuit from the HS circuitmap. Clear its HS
- * token, and remove it from the hashtable. */
- void
- hs_circuitmap_remove_circuit(circuit_t *circ)
- {
- tor_assert(the_hs_circuitmap);
- if (!circ || !circ->hs_token) {
- return;
- }
- /* Remove circ from circuitmap */
- circuit_t *tmp;
- tmp = HT_REMOVE(hs_circuitmap_ht, the_hs_circuitmap, circ);
- /* ... and ensure the removal was successful. */
- if (tmp) {
- tor_assert(tmp == circ);
- } else {
- log_warn(LD_BUG, "Could not find circuit (%u) in circuitmap.",
- circ->n_circ_id);
- }
- /* Clear token from circ */
- hs_token_free(circ->hs_token);
- circ->hs_token = NULL;
- }
- /* Public function: Initialize the global HS circuitmap. */
- void
- hs_circuitmap_init(void)
- {
- tor_assert(!the_hs_circuitmap);
- the_hs_circuitmap = tor_malloc_zero(sizeof(struct hs_circuitmap_ht));
- HT_INIT(hs_circuitmap_ht, the_hs_circuitmap);
- }
- /* Public function: Free all memory allocated by the global HS circuitmap. */
- void
- hs_circuitmap_free_all(void)
- {
- if (the_hs_circuitmap) {
- HT_CLEAR(hs_circuitmap_ht, the_hs_circuitmap);
- tor_free(the_hs_circuitmap);
- }
- }
|