hs_cache.c 11 KB

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