[tor-commits] [tor/master] prop224: Use state file to save/load revision counters

nickm at torproject.org nickm at torproject.org
Wed Aug 9 00:36:38 UTC 2017


commit 6f046b2191ed1a10e9058fcc49491b8db5a96280
Author: George Kadianakis <desnacked at riseup.net>
Date:   Fri Jul 21 15:53:17 2017 +0300

    prop224: Use state file to save/load revision counters
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 src/or/hs_service.c | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/or/or.h         |   3 +
 src/or/statefile.c  |   3 +
 3 files changed, 208 insertions(+), 7 deletions(-)

diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index f656592d5..6519cb1b6 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -23,6 +23,7 @@
 #include "router.h"
 #include "routerkeys.h"
 #include "routerlist.h"
+#include "statefile.h"
 
 #include "hs_circuit.h"
 #include "hs_common.h"
@@ -71,6 +72,8 @@ static const char *address_tld = "onion";
  * loading keys requires that we are an actual running tor process. */
 static smartlist_t *hs_service_staging_list;
 
+static void set_descriptor_revision_counter(hs_descriptor_t *hs_desc);
+
 /* Helper: Function to compare two objects in the service map. Return 1 if the
  * two service have the same master public identity key. */
 static inline int
@@ -1283,6 +1286,9 @@ build_service_descriptor(hs_service_t *service, time_t now,
     goto err;
   }
 
+  /* Set the revision counter for this descriptor */
+  set_descriptor_revision_counter(desc->desc);
+
   /* Let's make sure that we've created a descriptor that can actually be
    * encoded properly. This function also checks if the encoded output is
    * decodable after. */
@@ -1936,6 +1942,200 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
   return;
 }
 
+/** Return a newly-allocated string for our state file which contains revision
+ *  counter information for <b>desc</b>. The format is:
+ *
+ *     HidServRevCounter <blinded_pubkey> <rev_counter>
+ */
+static char *
+encode_desc_rev_counter_for_state(const hs_service_descriptor_t *desc)
+{
+  char *state_str = NULL;
+  char blinded_pubkey_b64[ED25519_BASE64_LEN+1];
+  uint64_t rev_counter = desc->desc->plaintext_data.revision_counter;
+  const ed25519_public_key_t *blinded_pubkey = &desc->blinded_kp.pubkey;
+
+  /* Turn the blinded key into b64 so that we save it on state */
+  tor_assert(blinded_pubkey);
+  if (ed25519_public_to_base64(blinded_pubkey_b64, blinded_pubkey) < 0) {
+    goto done;
+  }
+
+  /* Format is: <blinded key> <rev counter> */
+  tor_asprintf(&state_str, "%s %" PRIu64, blinded_pubkey_b64, rev_counter);
+
+  log_info(LD_GENERAL, "[!] Adding rev counter %" PRIu64 " for %s!",
+           rev_counter, blinded_pubkey_b64);
+
+ done:
+  return state_str;
+}
+
+/** Update HS descriptor revision counters in our state by removing the old
+ *  ones and writing down the ones that are currently active. */
+static void
+update_revision_counters_in_state(void)
+{
+  config_line_t *lines = NULL;
+  config_line_t **nextline = &lines;
+  or_state_t *state = get_or_state();
+
+  /* Prepare our state structure with the rev counters */
+  FOR_EACH_SERVICE_BEGIN(service) {
+    FOR_EACH_DESCRIPTOR_BEGIN(service, desc) {
+      /* We don't want to save zero counters */
+      if (desc->desc->plaintext_data.revision_counter == 0) {
+        continue;
+      }
+
+      *nextline = tor_malloc_zero(sizeof(config_line_t));
+      (*nextline)->key = tor_strdup("HidServRevCounter");
+      (*nextline)->value = encode_desc_rev_counter_for_state(desc);
+      nextline = &(*nextline)->next;
+    } FOR_EACH_DESCRIPTOR_END;
+  } FOR_EACH_SERVICE_END;
+
+  /* Remove the old rev counters, and replace them with the new ones */
+  config_free_lines(state->HidServRevCounters);
+  state->HidServRevCounters = lines;
+
+  /* Set the state as dirty since we just edited it */
+  if (!get_options()->AvoidDiskWrites) {
+    or_state_mark_dirty(state, 0);
+  }
+}
+
+/** Scan the string <b>state_line</b> for the revision counter of the service
+ *  with <b>blinded_pubkey</b>. Set <b>service_found_out</b> to True if the
+ *  line is relevant to this service, and return the cached revision
+ *  counter. Else set <b>service_found_out</b> to False. */
+static uint64_t
+check_state_line_for_service_rev_counter(const char *state_line,
+                                         ed25519_public_key_t *blinded_pubkey,
+                                         int *service_found_out)
+{
+  smartlist_t *items = NULL;
+  int ok;
+  ed25519_public_key_t pubkey_in_state;
+  uint64_t rev_counter = 0;
+
+  tor_assert(service_found_out);
+  tor_assert(state_line);
+  tor_assert(blinded_pubkey);
+
+  /* Assume that the line is not for this service */
+  *service_found_out = 0;
+
+  /* Start parsing the state line */
+  items = smartlist_new();
+  smartlist_split_string(items, state_line, NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+  if (smartlist_len(items) < 2) {
+    log_warn(LD_GENERAL, "Incomplete rev counter line. Ignoring.");
+    goto done;
+  }
+
+  char *b64_key_str = smartlist_get(items, 0);
+  char *saved_rev_counter_str = smartlist_get(items, 1);
+
+  /* Parse blinded key to check if it's for this hidden service */
+  if (ed25519_public_from_base64(&pubkey_in_state, b64_key_str) < 0) {
+    log_warn(LD_GENERAL, "Unable to base64 key in revcount line. Ignoring.");
+    goto done;
+  }
+  /* State line not for this hidden service */
+  if (!ed25519_pubkey_eq(&pubkey_in_state, blinded_pubkey)) {
+    goto done;
+  }
+
+  rev_counter = tor_parse_uint64(saved_rev_counter_str,
+                                 10, 0, UINT64_MAX, &ok, NULL);
+  if (!ok) {
+    log_warn(LD_GENERAL, "Unable to parse rev counter. Ignoring.");
+    goto done;
+  }
+
+  /* Since we got this far, the line was for this service */
+  *service_found_out = 1;
+
+  log_info(LD_GENERAL, "Found rev counter for %s: %" PRIu64,
+           b64_key_str, rev_counter);
+
+ done:
+  if (items) {
+    SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+    smartlist_free(items);
+  }
+
+  return rev_counter;
+}
+
+/** Dig into our state file and find the current revision counter for the
+ *  service with blinded key <b>blinded_pubkey</b>. If no revision counter is
+ *  found, return 0. */
+static uint64_t
+get_rev_counter_for_service(ed25519_public_key_t *blinded_pubkey)
+{
+  or_state_t *state = get_or_state();
+  config_line_t *line;
+
+  /* Set default value for rev counters (if not found) to 0 */
+  uint64_t final_rev_counter = 0;
+
+  for (line = state->HidServRevCounters ; line ; line = line->next) {
+    int service_found = 0;
+    uint64_t rev_counter = 0;
+
+    tor_assert(!strcmp(line->key, "HidServRevCounter"));
+
+    /* Scan all the HidServRevCounters lines till we find the line for this
+       service: */
+    rev_counter = check_state_line_for_service_rev_counter(line->value,
+                                                           blinded_pubkey,
+                                                           &service_found);
+    if (service_found) {
+      final_rev_counter = rev_counter;
+      goto done;
+    }
+  }
+
+ done:
+  return final_rev_counter;
+}
+
+/** Update the value of the revision counter for <b>hs_desc</b> and save it on
+    our state file. */
+static void
+update_descriptor_revision_counter(hs_descriptor_t *hs_desc)
+{
+  /* Find stored rev counter if it exists */
+  uint64_t rev_counter =
+    get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey);
+
+  /* Increment the revision counter of <b>hs_desc</b> so the next update (which
+   * will trigger an upload) will have the right value. We do this at this
+   * stage to only do it once because a descriptor can have many updates before
+   * being uploaded. By doing it at upload, we are sure to only increment by 1
+   * and thus avoid leaking how many operations we made on the descriptor from
+   * the previous one before uploading. */
+  rev_counter++;
+  hs_desc->plaintext_data.revision_counter = rev_counter;
+
+  update_revision_counters_in_state();
+}
+
+/** Set the revision counter in <b>hs_desc</b>, using the state file to find
+ *  the current counter value if it exists. */
+static void
+set_descriptor_revision_counter(hs_descriptor_t *hs_desc)
+{
+  /* Find stored rev counter if it exists */
+  uint64_t rev_counter =
+    get_rev_counter_for_service(&hs_desc->plaintext_data.blinded_pubkey);
+
+  hs_desc->plaintext_data.revision_counter = rev_counter;
+}
+
 /* Encode and sign the service descriptor desc and upload it to the
  * responsible hidden service directories. If for_next_period is true, the set
  * of directories are selected using the next hsdir_index. This does nothing
@@ -1992,13 +2192,8 @@ upload_descriptor_to_all(const hs_service_t *service,
               safe_str_client(service->onion_address), fmt_next_time);
   }
 
-  /* Increment the revision counter so the next update (which will trigger an
-   * upload) will have the right value. We do this at this stage to only do it
-   * once because a descriptor can have many updates before being uploaded. By
-   * doing it at upload, we are sure to only increment by 1 and thus avoid
-   * leaking how many operations we made on the descriptor from the previous
-   * one before uploading. */
-  desc->desc->plaintext_data.revision_counter += 1;
+  /* Update the revision counter of this descriptor */
+  update_descriptor_revision_counter(desc->desc);
 
   smartlist_free(responsible_dirs);
   return;
diff --git a/src/or/or.h b/src/or/or.h
index 09e58b7b1..d8aea3827 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -4626,6 +4626,9 @@ typedef struct {
 
   config_line_t *TransportProxies;
 
+  /** Cached revision counters for active hidden services on this host */
+  config_line_t *HidServRevCounters;
+
   /** These fields hold information on the history of bandwidth usage for
    * servers.  The "Ends" fields hold the time when we last updated the
    * bandwidth usage. The "Interval" fields hold the granularity, in seconds,
diff --git a/src/or/statefile.c b/src/or/statefile.c
index d0606b301..6b759960c 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -85,6 +85,9 @@ static config_var_t state_vars_[] = {
   VAR("TransportProxy",               LINELIST_S, TransportProxies, NULL),
   V(TransportProxies,                 LINELIST_V, NULL),
 
+  VAR("HidServRevCounter", LINELIST_S, HidServRevCounters, NULL),
+  V(HidServRevCounters,                 LINELIST_V, NULL),
+
   V(BWHistoryReadEnds,                ISOTIME,  NULL),
   V(BWHistoryReadInterval,            UINT,     "900"),
   V(BWHistoryReadValues,              CSV,      ""),





More information about the tor-commits mailing list