hs-v3: Add the Onion Balance config file option

At this commit, the service reads the config file and parse it to finally set
the service config object with the options.

Part of #32709

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2020-01-09 14:51:56 -05:00 committed by Nick Mathewson
parent f1498e75dd
commit ef28afa255
7 changed files with 289 additions and 2 deletions

View File

@ -510,6 +510,8 @@ static const config_var_t option_vars_[] = {
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceEnableIntroDoSBurstPerSec",
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceOnionBalanceInstance",
LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"),
V(HidServAuth, LINELIST, NULL),
V(ClientOnionAuthDir, FILENAME, NULL),

View File

@ -26,6 +26,7 @@
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_config.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_ob.h"
#include "feature/hs/hs_service.h"
#include "feature/rend/rendclient.h"
#include "feature/rend/rendservice.h"
@ -219,6 +220,7 @@ config_has_invalid_options(const config_line_t *line_,
"HiddenServiceEnableIntroDoSDefense",
"HiddenServiceEnableIntroDoSRatePerSec",
"HiddenServiceEnableIntroDoSBurstPerSec",
"HiddenServiceOnionBalanceInstance",
NULL /* End marker. */
};
@ -317,7 +319,7 @@ config_service_v3(const config_line_t *line_,
int have_num_ip = 0;
bool export_circuit_id = false; /* just to detect duplicate options */
bool dos_enabled = false, dos_rate_per_sec = false;
bool dos_burst_per_sec = false;
bool dos_burst_per_sec = false, ob_instance = false;
const char *dup_opt_seen = NULL;
const config_line_t *line;
@ -402,6 +404,27 @@ config_service_v3(const config_line_t *line_,
config->intro_dos_burst_per_sec);
continue;
}
if (!strcasecmp(line->key, "HiddenServiceOnionBalanceInstance")) {
bool enabled = !!helper_parse_uint64(line->key, line->value,
0, 1, &ok);
if (!ok || ob_instance) {
if (ob_instance) {
dup_opt_seen = line->key;
}
goto err;
}
ob_instance = true;
if (!enabled) {
/* Skip if this is disabled. */
continue;
}
/* Option is enabled, parse config file. */
ok = hs_ob_parse_config_file(config);
if (!ok) {
goto err;
}
continue;
}
}
/* We do not load the key material for the service at this stage. This is

222
src/feature/hs/hs_ob.c Normal file
View File

@ -0,0 +1,222 @@
/* Copyright (c) 2017-2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_ob.c
* \brief Implement Onion Balance specific code.
*
* \details
*
* XXX:
**/
#define HS_OB_PRIVATE
#include "lib/confmgt/confmgt.h"
#include "lib/encoding/confline.h"
#include "hs_ob.h"
/* Options config magic number. */
#define OB_OPTIONS_MAGIC 0x631DE7EA
/* Helper macros. */
#define VAR(varname, conftype, member, initvalue) \
CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue)
#define V(member,conftype,initvalue) \
VAR(#member, conftype, member, initvalue)
/* Dummy instance of ob_options_t, used for type-checking its members with
* CONF_CHECK_VAR_TYPE. */
DUMMY_TYPECHECK_INSTANCE(ob_options_t);
/* Array of variables for the config file options. */
static const config_var_t config_vars[] = {
V(MasterOnionAddress, LINELIST, NULL),
END_OF_CONFIG_VARS
};
/* "Extra" variable in the state that receives lines we can't parse. This
* lets us preserve options from versions of Tor newer than us. */
static const struct_member_t config_extra_vars = {
.name = "__extra",
.type = CONFIG_TYPE_LINELIST,
.offset = offsetof(ob_options_t, ExtraLines),
};
/* Configuration format of ob_options_t. */
static const config_format_t config_format = {
.size = sizeof(ob_options_t),
.magic = {
"ob_options_t",
OB_OPTIONS_MAGIC,
offsetof(ob_options_t, magic_),
},
.vars = config_vars,
.extra = &config_extra_vars,
};
/* Global configuration manager for the config file. */
static config_mgr_t *config_options_mgr = NULL;
/* Return the configuration manager for the config file. */
static const config_mgr_t *
get_config_options_mgr(void)
{
if (PREDICT_UNLIKELY(config_options_mgr == NULL)) {
config_options_mgr = config_mgr_new(&config_format);
config_mgr_freeze(config_options_mgr);
}
return config_options_mgr;
}
#define ob_option_free(val) \
FREE_AND_NULL(ob_options_t, ob_option_free_, (val))
/** Helper: Free a config options object. */
static void
ob_option_free_(ob_options_t *opts)
{
if (opts == NULL) {
return;
}
config_free(get_config_options_mgr(), opts);
}
/** Return an allocated config options object. */
static ob_options_t *
ob_option_new(void)
{
ob_options_t *opts = config_new(get_config_options_mgr());
config_init(get_config_options_mgr(), opts);
return opts;
}
/** Helper function: From the configuration line value which is an onion
* address with the ".onion" extension, find the public key and put it in
* pkey_out.
*
* On success, true is returned. Else, false and pkey is untouched. */
static bool
get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
{
char address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
tor_assert(value);
tor_assert(pkey_out);
if (strcmpend(value, ".onion")) {
/* Not a .onion extension, bad format. */
return false;
}
/* Length validation. The -1 is because sizeof() counts the NUL byte. */
if (strlen(value) >
(HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
/* Too long, bad format. */
return false;
}
/* We don't want the .onion so we add 2 because size - 1 is copied with
* strlcpy() in order to accomodate the NUL byte and sizeof() counts the NUL
* byte so we need to remove them from the equation. */
strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
return false;
}
/* Success. */
return true;
}
/** Parse the given ob options in opts and set the service config object
* accordingly.
*
* Return 1 on success else 0. */
static int
ob_option_parse(hs_service_config_t *config, const ob_options_t *opts)
{
int ret = 0;
config_line_t *line;
tor_assert(config);
tor_assert(opts);
for (line = opts->MasterOnionAddress; line; line = line->next) {
/* Allocate config list if need be. */
if (!config->ob_master_pubkeys) {
config->ob_master_pubkeys = smartlist_new();
}
ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey));
if (!get_onion_public_key(line->value, pubkey)) {
log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid",
line->value);
tor_free(pubkey);
goto end;
}
smartlist_add(config->ob_master_pubkeys, pubkey);
}
/* Success. */
ret = 1;
end:
/* No keys added, we free the list since no list means no onion balance
* support for this tor instance. */
if (smartlist_len(config->ob_master_pubkeys) == 0) {
smartlist_free(config->ob_master_pubkeys);
}
return ret;
}
/** Read and parse the config file at fname on disk. The service config object
* is populated with the options if any.
*
* Return 1 on success else 0. This is to follow the "ok" convention in
* hs_config.c. */
int
hs_ob_parse_config_file(hs_service_config_t *config)
{
static const char *fname = "ob_config";
int ret = 0;
char *content = NULL, *errmsg = NULL, *config_file_path = NULL;
ob_options_t *options = NULL;
config_line_t *lines = NULL;
tor_assert(config);
/* Read file from disk. */
config_file_path = hs_path_from_filename(config->directory_path, fname);
content = read_file_to_str(config_file_path, 0, NULL);
if (!content) {
log_warn(LD_FS, "OnionBalance: Unable to read config file %s",
escaped(config_file_path));
goto end;
}
/* Parse lines. */
if (config_get_lines(content, &lines, 0) < 0) {
goto end;
}
options = ob_option_new();
config_assign(get_config_options_mgr(), options, lines, 0, &errmsg);
if (errmsg) {
log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s",
errmsg);
tor_free(errmsg);
goto end;
}
/* Parse the options and set the service config object with the details. */
ret = ob_option_parse(config, options);
end:
config_free_lines(lines);
ob_option_free(options);
tor_free(content);
tor_free(config_file_path);
return ret;
}

29
src/feature/hs/hs_ob.h Normal file
View File

@ -0,0 +1,29 @@
/* Copyright (c) 2020, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file hs_ob.h
* \brief Header file for the specific code for onion balance.
**/
#ifndef TOR_HS_OB_H
#define TOR_HS_OB_H
#include "hs_service.h"
int hs_ob_parse_config_file(hs_service_config_t *config);
#ifdef HS_OB_PRIVATE
typedef struct ob_options_t {
/** Magic number to identify the structure in memory. */
uint32_t magic_;
/** Master Onion Address(es). */
struct config_line_t *MasterOnionAddress;
/** Extra Lines for configuration we might not know. */
struct config_line_t *ExtraLines;
} ob_options_t;
#endif /* defined(HS_OB_PRIVATE) */
#endif /* !defined(TOR_HS_OB_H) */

View File

@ -267,6 +267,11 @@ service_clear_config(hs_service_config_t *config)
service_authorized_client_free(p));
smartlist_free(config->clients);
}
if (config->ob_master_pubkeys) {
SMARTLIST_FOREACH(config->ob_master_pubkeys, ed25519_public_key_t *, k,
tor_free(k));
smartlist_free(config->ob_master_pubkeys);
}
memset(config, 0, sizeof(*config));
}

View File

@ -248,10 +248,14 @@ typedef struct hs_service_config_t {
/** Does this service export the circuit ID of its clients? */
hs_circuit_id_protocol_t circuit_id_protocol;
/* DoS defenses. For the ESTABLISH_INTRO cell extension. */
/** DoS defenses. For the ESTABLISH_INTRO cell extension. */
unsigned int has_dos_defense_enabled : 1;
uint32_t intro_dos_rate_per_sec;
uint32_t intro_dos_burst_per_sec;
/** If set, contains the Onion Balance master ed25519 public key (taken from
* an .onion addresses) that this tor instance serves as backend. */
smartlist_t *ob_master_pubkeys;
} hs_service_config_t;
/** Service state. */

View File

@ -13,6 +13,7 @@ LIBTOR_APP_A_SOURCES += \
src/feature/hs/hs_dos.c \
src/feature/hs/hs_ident.c \
src/feature/hs/hs_intropoint.c \
src/feature/hs/hs_ob.c \
src/feature/hs/hs_service.c \
src/feature/hs/hs_stats.c
@ -30,6 +31,7 @@ noinst_HEADERS += \
src/feature/hs/hs_dos.h \
src/feature/hs/hs_ident.h \
src/feature/hs/hs_intropoint.h \
src/feature/hs/hs_ob.h \
src/feature/hs/hs_service.h \
src/feature/hs/hs_stats.h \
src/feature/hs/hsdir_index_st.h