hs_cache.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* Copyright (c) 2016, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. /**
  4. * \file hs_cache.c
  5. * \brief Handle hidden service descriptor caches.
  6. **/
  7. #include "hs_cache.h"
  8. #include "or.h"
  9. #include "config.h"
  10. #include "hs_common.h"
  11. #include "hs_descriptor.h"
  12. #include "rendcache.h"
  13. /* Directory descriptor cache. Map indexed by blinded key. */
  14. static digest256map_t *hs_cache_v3_dir;
  15. /* Remove a given descriptor from our cache. */
  16. static void
  17. remove_v3_desc_as_dir(const hs_cache_dir_descriptor_t *desc)
  18. {
  19. tor_assert(desc);
  20. digest256map_remove(hs_cache_v3_dir, desc->key);
  21. }
  22. /* Store a given descriptor in our cache. */
  23. static void
  24. store_v3_desc_as_dir(hs_cache_dir_descriptor_t *desc)
  25. {
  26. tor_assert(desc);
  27. digest256map_set(hs_cache_v3_dir, desc->key, desc);
  28. }
  29. /* Query our cache and return the entry or NULL if not found. */
  30. static hs_cache_dir_descriptor_t *
  31. lookup_v3_desc_as_dir(const uint8_t *key)
  32. {
  33. tor_assert(key);
  34. return digest256map_get(hs_cache_v3_dir, key);
  35. }
  36. /* Free a directory descriptor object. */
  37. static void
  38. cache_dir_desc_free(hs_cache_dir_descriptor_t *desc)
  39. {
  40. if (desc == NULL) {
  41. return;
  42. }
  43. hs_desc_plaintext_data_free(desc->plaintext_data);
  44. tor_free(desc->encoded_desc);
  45. tor_free(desc);
  46. }
  47. /* Create a new directory cache descriptor object from a encoded descriptor.
  48. * On success, return the heap-allocated cache object, otherwise return NULL if
  49. * we can't decode the descriptor. */
  50. static hs_cache_dir_descriptor_t *
  51. cache_dir_desc_new(const char *desc)
  52. {
  53. hs_cache_dir_descriptor_t *dir_desc;
  54. tor_assert(desc);
  55. dir_desc = tor_malloc_zero(sizeof(hs_cache_dir_descriptor_t));
  56. dir_desc->plaintext_data =
  57. tor_malloc_zero(sizeof(hs_desc_plaintext_data_t));
  58. dir_desc->encoded_desc = tor_strdup(desc);
  59. if (hs_desc_decode_plaintext(desc, dir_desc->plaintext_data) < 0) {
  60. log_debug(LD_DIR, "Unable to decode descriptor. Rejecting.");
  61. goto err;
  62. }
  63. /* The blinded pubkey is the indexed key. */
  64. dir_desc->key = dir_desc->plaintext_data->blinded_kp.pubkey.pubkey;
  65. dir_desc->created_ts = time(NULL);
  66. return dir_desc;
  67. err:
  68. cache_dir_desc_free(dir_desc);
  69. return NULL;
  70. }
  71. /* Return the size of a cache entry in bytes. */
  72. static size_t
  73. cache_get_entry_size(const hs_cache_dir_descriptor_t *entry)
  74. {
  75. return (sizeof(*entry) + hs_desc_plaintext_obj_size(entry->plaintext_data)
  76. + strlen(entry->encoded_desc));
  77. }
  78. /* Try to store a valid version 3 descriptor in the directory cache. Return 0
  79. * on success else a negative value is returned indicating that we have a
  80. * newer version in our cache. On error, caller is responsible to free the
  81. * given descriptor desc. */
  82. static int
  83. cache_store_v3_as_dir(hs_cache_dir_descriptor_t *desc)
  84. {
  85. hs_cache_dir_descriptor_t *cache_entry;
  86. tor_assert(desc);
  87. /* Verify if we have an entry in the cache for that key and if yes, check
  88. * if we should replace it? */
  89. cache_entry = lookup_v3_desc_as_dir(desc->key);
  90. if (cache_entry != NULL) {
  91. /* Only replace descriptor if revision-counter is greater than the one
  92. * in our cache */
  93. if (cache_entry->plaintext_data->revision_counter >=
  94. desc->plaintext_data->revision_counter) {
  95. log_info(LD_REND, "Descriptor revision counter in our cache is "
  96. "greater or equal than the one we received. "
  97. "Rejecting!");
  98. goto err;
  99. }
  100. /* We now know that the descriptor we just received is a new one so
  101. * remove the entry we currently have from our cache so we can then
  102. * store the new one. */
  103. remove_v3_desc_as_dir(cache_entry);
  104. cache_dir_desc_free(cache_entry);
  105. rend_cache_decrement_allocation(cache_get_entry_size(cache_entry));
  106. }
  107. /* Store the descriptor we just got. We are sure here that either we
  108. * don't have the entry or we have a newer descriptor and the old one
  109. * has been removed from the cache. */
  110. store_v3_desc_as_dir(desc);
  111. /* Update our total cache size with this entry for the OOM. This uses the
  112. * old HS protocol cache subsystem for which we are tied with. */
  113. rend_cache_increment_allocation(cache_get_entry_size(desc));
  114. /* XXX: Update HS statistics. We should have specific stats for v3. */
  115. return 0;
  116. err:
  117. return -1;
  118. }
  119. /* Using the query which is the blinded key for a descriptor version 3, lookup
  120. * in our directory cache the entry. If found, 1 is returned and desc_out is
  121. * populated with a newly allocated string being the encoded descriptor. If
  122. * not found, 0 is returned and desc_out is untouched. On error, a negative
  123. * value is returned and desc_out is untouched. */
  124. static int
  125. cache_lookup_v3_as_dir(const char *query, char **desc_out)
  126. {
  127. int found = 0;
  128. ed25519_public_key_t blinded_key;
  129. const hs_cache_dir_descriptor_t *entry;
  130. tor_assert(query);
  131. /* Decode blinded key using the given query value. */
  132. if (ed25519_public_from_base64(&blinded_key, query) < 0) {
  133. log_info(LD_REND, "Unable to decode the v3 HSDir query %s.",
  134. safe_str_client(query));
  135. goto err;
  136. }
  137. entry = lookup_v3_desc_as_dir(blinded_key.pubkey);
  138. if (entry != NULL) {
  139. found = 1;
  140. if (desc_out) {
  141. *desc_out = tor_strdup(entry->encoded_desc);
  142. }
  143. }
  144. return found;
  145. err:
  146. return -1;
  147. }
  148. /* Clean the v3 cache by removing any entry that has expired using the
  149. * <b>global_cutoff</b> value. If <b>global_cutoff</b> is 0, the cleaning
  150. * process will use the lifetime found in the plaintext data section. Return
  151. * the number of bytes cleaned. */
  152. static size_t
  153. cache_clean_v3_as_dir(time_t now, time_t global_cutoff)
  154. {
  155. size_t bytes_removed = 0;
  156. /* Code flow error if this ever happens. */
  157. tor_assert(global_cutoff >= 0);
  158. if (!hs_cache_v3_dir) { /* No cache to clean. Just return. */
  159. return 0;
  160. }
  161. DIGEST256MAP_FOREACH_MODIFY(hs_cache_v3_dir, key,
  162. hs_cache_dir_descriptor_t *, entry) {
  163. size_t entry_size;
  164. time_t cutoff = global_cutoff;
  165. if (!cutoff) {
  166. /* Cutoff is the lifetime of the entry found in the descriptor. */
  167. cutoff = now - entry->plaintext_data->lifetime_sec;
  168. }
  169. /* If the entry has been created _after_ the cutoff, not expired so
  170. * continue to the next entry in our v3 cache. */
  171. if (entry->created_ts > cutoff) {
  172. continue;
  173. }
  174. /* Here, our entry has expired, remove and free. */
  175. MAP_DEL_CURRENT(key);
  176. entry_size = cache_get_entry_size(entry);
  177. bytes_removed += entry_size;
  178. /* Entry is not in the cache anymore, destroy it. */
  179. cache_dir_desc_free(entry);
  180. /* Update our cache entry allocation size for the OOM. */
  181. rend_cache_decrement_allocation(entry_size);
  182. /* Logging. */
  183. {
  184. char key_b64[BASE64_DIGEST256_LEN + 1];
  185. base64_encode(key_b64, sizeof(key_b64), (const char *) key,
  186. DIGEST256_LEN, 0);
  187. log_info(LD_REND, "Removing v3 descriptor '%s' from HSDir cache",
  188. safe_str_client(key_b64));
  189. }
  190. } DIGEST256MAP_FOREACH_END;
  191. return bytes_removed;
  192. }
  193. /* Given an encoded descriptor, store it in the directory cache depending on
  194. * which version it is. Return a negative value on error. On success, 0 is
  195. * returned. */
  196. int
  197. hs_cache_store_as_dir(const char *desc)
  198. {
  199. hs_cache_dir_descriptor_t *dir_desc = NULL;
  200. tor_assert(desc);
  201. /* Create a new cache object. This can fail if the descriptor plaintext data
  202. * is unparseable which in this case a log message will be triggered. */
  203. dir_desc = cache_dir_desc_new(desc);
  204. if (dir_desc == NULL) {
  205. goto err;
  206. }
  207. /* Call the right function against the descriptor version. At this point,
  208. * we are sure that the descriptor's version is supported else the
  209. * decoding would have failed. */
  210. switch (dir_desc->plaintext_data->version) {
  211. case 3:
  212. default:
  213. if (cache_store_v3_as_dir(dir_desc) < 0) {
  214. goto err;
  215. }
  216. break;
  217. }
  218. return 0;
  219. err:
  220. cache_dir_desc_free(dir_desc);
  221. return -1;
  222. }
  223. /* Using the query, lookup in our directory cache the entry. If found, 1 is
  224. * returned and desc_out is populated with a newly allocated string being
  225. * the encoded descriptor. If not found, 0 is returned and desc_out is
  226. * untouched. On error, a negative value is returned and desc_out is
  227. * untouched. */
  228. int
  229. hs_cache_lookup_as_dir(uint32_t version, const char *query,
  230. char **desc_out)
  231. {
  232. int found;
  233. tor_assert(query);
  234. /* This should never be called with an unsupported version. */
  235. tor_assert(hs_desc_is_supported_version(version));
  236. switch (version) {
  237. case 3:
  238. default:
  239. found = cache_lookup_v3_as_dir(query, desc_out);
  240. break;
  241. }
  242. return found;
  243. }
  244. /* Clean all directory caches using the current time now. */
  245. void
  246. hs_cache_clean_as_dir(time_t now)
  247. {
  248. time_t cutoff;
  249. /* Start with v2 cache cleaning. */
  250. cutoff = now - rend_cache_max_entry_lifetime();
  251. rend_cache_clean_v2_descs_as_dir(cutoff);
  252. /* Now, clean the v3 cache. Set the cutoff to 0 telling the cleanup function
  253. * to compute the cutoff by itself using the lifetime value. */
  254. cache_clean_v3_as_dir(now, 0);
  255. }
  256. /* Do a round of OOM cleanup on all directory caches. Return the amount of
  257. * removed bytes. It is possible that the returned value is lower than
  258. * min_remove_bytes if the caches get emptied out so the caller should be
  259. * aware of this. */
  260. size_t
  261. hs_cache_handle_oom(time_t now, size_t min_remove_bytes)
  262. {
  263. time_t k;
  264. size_t bytes_removed = 0;
  265. /* Our OOM handler called with 0 bytes to remove is a code flow error. */
  266. tor_assert(min_remove_bytes != 0);
  267. /* The algorithm is as follow. K is the oldest expected descriptor age.
  268. *
  269. * 1) Deallocate all entries from v2 cache that are older than K hours.
  270. * 1.1) If the amount of remove bytes has been reached, stop.
  271. * 2) Deallocate all entries from v3 cache that are older than K hours
  272. * 2.1) If the amount of remove bytes has been reached, stop.
  273. * 3) Set K = K - RendPostPeriod and repeat process until K is < 0.
  274. *
  275. * This ends up being O(Kn).
  276. */
  277. /* Set K to the oldest expected age in seconds which is the maximum
  278. * lifetime of a cache entry. We'll use the v2 lifetime because it's much
  279. * bigger than the v3 thus leading to cleaning older descriptors. */
  280. k = rend_cache_max_entry_lifetime();
  281. do {
  282. time_t cutoff;
  283. /* If K becomes negative, it means we've empty the caches so stop and
  284. * return what we were able to cleanup. */
  285. if (k < 0) {
  286. break;
  287. }
  288. /* Compute a cutoff value with K and the current time. */
  289. cutoff = now - k;
  290. /* Start by cleaning the v2 cache with that cutoff. */
  291. bytes_removed += rend_cache_clean_v2_descs_as_dir(cutoff);
  292. if (bytes_removed < min_remove_bytes) {
  293. /* We haven't remove enough bytes so clean v3 cache. */
  294. bytes_removed += cache_clean_v3_as_dir(now, cutoff);
  295. /* Decrement K by a post period to shorten the cutoff. */
  296. k -= get_options()->RendPostPeriod;
  297. }
  298. } while (bytes_removed < min_remove_bytes);
  299. return bytes_removed;
  300. }
  301. /* Initialize the hidden service cache subsystem. */
  302. void
  303. hs_cache_init(void)
  304. {
  305. /* Calling this twice is very wrong code flow. */
  306. tor_assert(!hs_cache_v3_dir);
  307. hs_cache_v3_dir = digest256map_new();
  308. }