mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 12:23:32 +01:00
prop224: Use state file to save/load revision counters
Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
2e5a2d64bd
commit
6f046b2191
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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, ""),
|
||||
|
Loading…
Reference in New Issue
Block a user