hs_circuitmap.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* Copyright (c) 2016, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file hs_circuitmap.c
  5. *
  6. * \brief Manage the hidden service circuitmap: A hash table that maps binary
  7. * tokens to introduction and rendezvous circuits.
  8. **/
  9. #define HS_CIRCUITMAP_PRIVATE
  10. #include "or.h"
  11. #include "config.h"
  12. #include "circuitlist.h"
  13. #include "hs_circuitmap.h"
  14. /************************** HS circuitmap code *******************************/
  15. /* This is the hidden service circuitmap. It's a hash table that maps
  16. introduction and rendezvous tokens to specific circuits such that given a
  17. token it's easy to find the corresponding circuit. */
  18. static struct hs_circuitmap_ht *the_hs_circuitmap = NULL;
  19. /* This is a helper function used by the hash table code (HT_). It returns 1 if
  20. * two circuits have the same HS token. */
  21. static int
  22. hs_circuits_have_same_token(const or_circuit_t *first_circuit,
  23. const or_circuit_t *second_circuit)
  24. {
  25. const hs_token_t *first_token;
  26. const hs_token_t *second_token;
  27. tor_assert(first_circuit);
  28. tor_assert(second_circuit);
  29. first_token = first_circuit->hs_token;
  30. second_token = second_circuit->hs_token;
  31. /* Both circs must have a token */
  32. if (BUG(!first_token) || BUG(!second_token)) {
  33. return 0;
  34. }
  35. if (first_token->type != second_token->type) {
  36. return 0;
  37. }
  38. if (first_token->token_len != second_token->token_len)
  39. return 0;
  40. return tor_memeq(first_token->token,
  41. second_token->token,
  42. first_token->token_len);
  43. }
  44. /* This is a helper function for the hash table code (HT_). It hashes a circuit
  45. * HS token into an unsigned int for use as a key by the hash table routines.*/
  46. static inline unsigned int
  47. hs_circuit_hash_token(const or_circuit_t *circuit)
  48. {
  49. tor_assert(circuit->hs_token);
  50. return (unsigned) siphash24g(circuit->hs_token->token,
  51. circuit->hs_token->token_len);
  52. }
  53. /* Register the circuitmap hash table */
  54. HT_PROTOTYPE(hs_circuitmap_ht, // The name of the hashtable struct
  55. or_circuit_t, // The name of the element struct,
  56. hs_circuitmap_node, // The name of HT_ENTRY member
  57. hs_circuit_hash_token, hs_circuits_have_same_token)
  58. HT_GENERATE2(hs_circuitmap_ht, or_circuit_t, hs_circuitmap_node,
  59. hs_circuit_hash_token, hs_circuits_have_same_token,
  60. 0.6, tor_reallocarray, tor_free_)
  61. #ifdef TOR_UNIT_TESTS
  62. /* Return the global HS circuitmap. Used by unittests. */
  63. hs_circuitmap_ht *
  64. get_hs_circuitmap(void)
  65. {
  66. return the_hs_circuitmap;
  67. }
  68. #endif
  69. /****************** HS circuitmap utility functions **************************/
  70. /** Return a new HS token of type <b>type</b> containing <b>token</b>. */
  71. static hs_token_t *
  72. hs_token_new(hs_token_type_t type, size_t token_len,
  73. const uint8_t *token)
  74. {
  75. tor_assert(token);
  76. hs_token_t *hs_token = tor_malloc_zero(sizeof(hs_token_t));
  77. hs_token->type = type;
  78. hs_token->token_len = token_len;
  79. hs_token->token = tor_memdup(token, token_len);
  80. return hs_token;
  81. }
  82. /** Free memory allocated by this <b>hs_token</b>. */
  83. static void
  84. hs_token_free(hs_token_t *hs_token)
  85. {
  86. if (!hs_token) {
  87. return;
  88. }
  89. tor_free(hs_token->token);
  90. tor_free(hs_token);
  91. }
  92. /** Return the circuit from the circuitmap with token <b>search_token</b>. */
  93. static or_circuit_t *
  94. get_circuit_with_token(hs_token_t *search_token)
  95. {
  96. tor_assert(the_hs_circuitmap);
  97. /* We use a dummy circuit object for the hash table search routine. */
  98. or_circuit_t search_circ;
  99. search_circ.hs_token = search_token;
  100. return HT_FIND(hs_circuitmap_ht, the_hs_circuitmap, &search_circ);
  101. }
  102. /* Helper function that registers <b>circ</b> with <b>token</b> on the HS
  103. circuitmap. This function steals reference of <b>token</b>. */
  104. static void
  105. hs_circuitmap_register_impl(or_circuit_t *circ, hs_token_t *token)
  106. {
  107. tor_assert(circ);
  108. tor_assert(token);
  109. tor_assert(the_hs_circuitmap);
  110. /* If this circuit already has a token, clear it. */
  111. if (circ->hs_token) {
  112. hs_circuitmap_remove_circuit(circ);
  113. }
  114. /* Kill old circuits with the same token. We want new intro/rend circuits to
  115. take precedence over old ones, so that HSes and clients and reestablish
  116. killed circuits without changing the HS token. */
  117. {
  118. or_circuit_t *found_circ;
  119. found_circ = get_circuit_with_token(token);
  120. if (found_circ) {
  121. hs_circuitmap_remove_circuit(found_circ);
  122. if (!found_circ->base_.marked_for_close) {
  123. circuit_mark_for_close(TO_CIRCUIT(found_circ),
  124. END_CIRC_REASON_FINISHED);
  125. }
  126. }
  127. }
  128. /* Register circuit and token to circuitmap. */
  129. circ->hs_token = token;
  130. HT_INSERT(hs_circuitmap_ht, the_hs_circuitmap, circ);
  131. }
  132. /** Helper function: Register <b>circ</b> of <b>type</b> on the HS
  133. * circuitmap. Use the HS <b>token</b> as the key to the hash table. If
  134. * <b>token</b> is not set, clear the circuit of any HS tokens. */
  135. static void
  136. hs_circuitmap_register_circuit(or_circuit_t *circ,
  137. hs_token_type_t type, size_t token_len,
  138. const uint8_t *token)
  139. {
  140. hs_token_t *hs_token = NULL;
  141. /* Create a new token and register it to the circuitmap */
  142. tor_assert(token);
  143. hs_token = hs_token_new(type, token_len, token);
  144. tor_assert(hs_token);
  145. hs_circuitmap_register_impl(circ, hs_token);
  146. }
  147. /* Query circuitmap for circuit with <b>token</b> of size <b>token_len</b>.
  148. * Only returns a circuit with purpose equal to the <b>wanted_circ_purpose</b>
  149. * parameter and if it is NOT marked for close. Return NULL if no such circuit
  150. * is found. */
  151. static or_circuit_t *
  152. hs_circuitmap_get_circuit(hs_token_type_t type,
  153. size_t token_len,
  154. const uint8_t *token,
  155. uint8_t wanted_circ_purpose)
  156. {
  157. or_circuit_t *found_circ = NULL;
  158. tor_assert(the_hs_circuitmap);
  159. /* Check the circuitmap if we have a circuit with this token */
  160. {
  161. hs_token_t *search_hs_token = hs_token_new(type, token_len, token);
  162. tor_assert(search_hs_token);
  163. found_circ = get_circuit_with_token(search_hs_token);
  164. hs_token_free(search_hs_token);
  165. }
  166. /* Check that the circuit is useful to us */
  167. if (!found_circ ||
  168. found_circ->base_.purpose != wanted_circ_purpose ||
  169. found_circ->base_.marked_for_close) {
  170. return NULL;
  171. }
  172. return found_circ;
  173. }
  174. /************** Public circuitmap API ****************************************/
  175. /* Public function: Return v3 introduction circuit with <b>auth_key</b>. Return
  176. * NULL if no such circuit is found in the circuitmap. */
  177. or_circuit_t *
  178. hs_circuitmap_get_intro_circ_v3(const ed25519_public_key_t *auth_key)
  179. {
  180. tor_assert(auth_key);
  181. return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V3,
  182. ED25519_PUBKEY_LEN, auth_key->pubkey,
  183. CIRCUIT_PURPOSE_INTRO_POINT);
  184. }
  185. /* Public function: Return v2 introduction circuit with <b>digest</b>. Return
  186. * NULL if no such circuit is found in the circuitmap. */
  187. or_circuit_t *
  188. hs_circuitmap_get_intro_circ_v2(const uint8_t *digest)
  189. {
  190. tor_assert(digest);
  191. return hs_circuitmap_get_circuit(HS_TOKEN_INTRO_V2,
  192. REND_TOKEN_LEN, digest,
  193. CIRCUIT_PURPOSE_INTRO_POINT);
  194. }
  195. /* Public function: Return rendezvous circuit with rendezvous
  196. * <b>cookie</b>. Return NULL if no such circuit is found in the circuitmap. */
  197. or_circuit_t *
  198. hs_circuitmap_get_rend_circ(const uint8_t *cookie)
  199. {
  200. tor_assert(cookie);
  201. return hs_circuitmap_get_circuit(HS_TOKEN_REND,
  202. REND_TOKEN_LEN, cookie,
  203. CIRCUIT_PURPOSE_REND_POINT_WAITING);
  204. }
  205. /* Public function: Register rendezvous circuit with key <b>cookie</b> to the
  206. * circuitmap. */
  207. void
  208. hs_circuitmap_register_rend_circ(or_circuit_t *circ, const uint8_t *cookie)
  209. {
  210. hs_circuitmap_register_circuit(circ,
  211. HS_TOKEN_REND,
  212. REND_TOKEN_LEN, cookie);
  213. }
  214. /* Public function: Register v2 intro circuit with key <b>digest</b> to the
  215. * circuitmap. */
  216. void
  217. hs_circuitmap_register_intro_circ_v2(or_circuit_t *circ, const uint8_t *digest)
  218. {
  219. hs_circuitmap_register_circuit(circ,
  220. HS_TOKEN_INTRO_V2,
  221. REND_TOKEN_LEN, digest);
  222. }
  223. /* Public function: Register v3 intro circuit with key <b>auth_key</b> to the
  224. * circuitmap. */
  225. void
  226. hs_circuitmap_register_intro_circ_v3(or_circuit_t *circ,
  227. const ed25519_public_key_t *auth_key)
  228. {
  229. hs_circuitmap_register_circuit(circ,
  230. HS_TOKEN_INTRO_V3,
  231. ED25519_PUBKEY_LEN, auth_key->pubkey);
  232. }
  233. /** Remove this circuit from the HS circuitmap. Clear its HS token, and remove
  234. * it from the hashtable. */
  235. void
  236. hs_circuitmap_remove_circuit(or_circuit_t *circ)
  237. {
  238. tor_assert(the_hs_circuitmap);
  239. if (!circ || !circ->hs_token) {
  240. return;
  241. }
  242. /* Remove circ from circuitmap */
  243. or_circuit_t *tmp;
  244. tmp = HT_REMOVE(hs_circuitmap_ht, the_hs_circuitmap, circ);
  245. /* ... and ensure the removal was successful. */
  246. if (tmp) {
  247. tor_assert(tmp == circ);
  248. } else {
  249. log_warn(LD_BUG, "Could not find circuit (%u) in circuitmap.",
  250. circ->p_circ_id);
  251. }
  252. /* Clear token from circ */
  253. hs_token_free(circ->hs_token);
  254. circ->hs_token = NULL;
  255. }
  256. /* Initialize the global HS circuitmap. */
  257. void
  258. hs_circuitmap_init(void)
  259. {
  260. tor_assert(!the_hs_circuitmap);
  261. the_hs_circuitmap = tor_malloc_zero(sizeof(struct hs_circuitmap_ht));
  262. HT_INIT(hs_circuitmap_ht, the_hs_circuitmap);
  263. }
  264. /* Free all memory allocated by the global HS circuitmap. */
  265. void
  266. hs_circuitmap_free_all(void)
  267. {
  268. if (the_hs_circuitmap) {
  269. HT_CLEAR(hs_circuitmap_ht, the_hs_circuitmap);
  270. tor_free(the_hs_circuitmap);
  271. }
  272. }