prop224: Use state file to save/load revision counters

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
George Kadianakis 2017-07-21 15:53:17 +03:00 committed by Nick Mathewson
parent 2e5a2d64bd
commit 6f046b2191
3 changed files with 208 additions and 7 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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, ""),