commit 6f046b2191ed1a10e9058fcc49491b8db5a96280 Author: George Kadianakis desnacked@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@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, ""),
tor-commits@lists.torproject.org