#define HS_OB_PRIVATE
#include "feature/hs/hs_service.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/networkstatus_st.h"
#include "lib/confmgt/confmgt.h"
#include "lib/encoding/confline.h"
#include "feature/hs/hs_ob.h"
#define OB_OPTIONS_MAGIC 0x631DE7EA
#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_TYPECHECK_INSTANCE(ob_options_t);
static const config_var_t config_vars[] = {
V(MasterOnionAddress, LINELIST, NULL),
END_OF_CONFIG_VARS
};
static const struct_member_t config_extra_vars = {
.name = "__extra",
.type = CONFIG_TYPE_LINELIST,
.offset = offsetof(ob_options_t, ExtraLines),
};
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,
};
static config_mgr_t *config_options_mgr = NULL;
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))
static void
ob_option_free_(ob_options_t *opts)
{
if (opts == NULL) {
return;
}
config_free(get_config_options_mgr(), opts);
}
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;
}
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")) {
return false;
}
if (strlen(value) >
(HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
return false;
}
strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
return false;
}
return true;
}
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) {
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);
log_notice(LD_REND, "OnionBalance: MasterOnionAddress %s registered",
line->value);
}
ret = 1;
end:
if (smartlist_len(config->ob_master_pubkeys) == 0) {
smartlist_free(config->ob_master_pubkeys);
}
return ret;
}
static void
build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp,
hs_subcredential_t *subcredential)
{
ed25519_public_key_t blinded_pubkey;
tor_assert(pkey);
tor_assert(subcredential);
hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey);
hs_get_subcredential(pkey, &blinded_pubkey, subcredential);
}
bool
hs_ob_service_is_instance(const hs_service_t *service)
{
if (BUG(service == NULL)) {
return false;
}
if (!service->config.ob_master_pubkeys) {
return false;
}
return smartlist_len(service->config.ob_master_pubkeys) > 0;
}
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);
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;
}
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;
}
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;
}
STATIC size_t
compute_subcredentials(const hs_service_t *service,
hs_subcredential_t **subcredentials_out)
{
unsigned int num_pkeys, idx = 0;
hs_subcredential_t *subcreds = NULL;
const int steps[3] = {0, -1, 1};
const unsigned int num_steps = ARRAY_LENGTH(steps);
const uint64_t tp = hs_get_time_period_num(0);
tor_assert(service);
tor_assert(subcredentials_out);
tor_assert(service->desc_current);
tor_assert(service->desc_next);
num_pkeys = smartlist_len(service->config.ob_master_pubkeys);
if (!num_pkeys) {
*subcredentials_out = NULL;
return 0;
}
subcreds =
tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t));
for (unsigned int i = 0; i < num_steps; i++) {
SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys,
const ed25519_public_key_t *, pkey) {
build_subcredential(pkey, tp + steps[i], &subcreds[idx]);
idx++;
} SMARTLIST_FOREACH_END(pkey);
}
memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential,
sizeof(hs_subcredential_t));
memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential,
sizeof(hs_subcredential_t));
log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).",
idx, (int)tp);
*subcredentials_out = subcreds;
return idx;
}
void
hs_ob_refresh_keys(hs_service_t *service)
{
hs_subcredential_t *ob_subcreds = NULL;
size_t num_subcreds;
tor_assert(service);
if (!hs_ob_service_is_instance(service)) {
return;
}
if (!service->desc_current || !service->desc_next) {
return;
}
num_subcreds = compute_subcredentials(service, &ob_subcreds);
if (BUG(!num_subcreds)) {
return;
}
if (service->state.ob_subcreds) {
tor_free(service->state.ob_subcreds);
}
service->state.ob_subcreds = ob_subcreds;
service->state.n_ob_subcreds = num_subcreds;
}
void
hs_ob_free_all(void)
{
config_mgr_free(config_options_mgr);
}