mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2025-02-24 22:58:50 +01:00
hs-v3: Load all client auth keys to the service
This commit loads all client public keys from every file in `authorized_clients/` directory. Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
94605f08fb
commit
b894b40e64
2 changed files with 227 additions and 1 deletions
|
@ -88,6 +88,7 @@
|
|||
|
||||
/* Onion service directory file names. */
|
||||
static const char fname_keyfile_prefix[] = "hs_ed25519";
|
||||
static const char dname_client_pubkeys[] = "authorized_clients";
|
||||
static const char fname_hostname[] = "hostname";
|
||||
static const char address_tld[] = "onion";
|
||||
|
||||
|
@ -103,6 +104,7 @@ static smartlist_t *hs_service_staging_list;
|
|||
static int consider_republishing_hs_descriptors = 0;
|
||||
|
||||
/* Static declaration. */
|
||||
static int load_client_keys(hs_service_t *service);
|
||||
static void set_descriptor_revision_counter(hs_service_descriptor_t *hs_desc,
|
||||
time_t now, bool is_current);
|
||||
static void move_descriptors(hs_service_t *src, hs_service_t *dst);
|
||||
|
@ -247,6 +249,11 @@ service_clear_config(hs_service_config_t *config)
|
|||
rend_service_port_config_free(p););
|
||||
smartlist_free(config->ports);
|
||||
}
|
||||
if (config->clients) {
|
||||
SMARTLIST_FOREACH(config->clients, hs_service_authorized_client_t *, p,
|
||||
service_authorized_client_free(p));
|
||||
smartlist_free(config->clients);
|
||||
}
|
||||
memset(config, 0, sizeof(*config));
|
||||
}
|
||||
|
||||
|
@ -1070,6 +1077,11 @@ load_service_keys(hs_service_t *service)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Load all client authorization keys in the service. */
|
||||
if (load_client_keys(service) < 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Succes. */
|
||||
ret = 0;
|
||||
end:
|
||||
|
@ -1077,6 +1089,200 @@ load_service_keys(hs_service_t *service)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Check if the client file name is valid or not. Return 1 if valid,
|
||||
* otherwise return 0. */
|
||||
static int
|
||||
client_filename_is_valid(const char *filename)
|
||||
{
|
||||
int ret = 1;
|
||||
const char *valid_extension = ".auth";
|
||||
|
||||
tor_assert(filename);
|
||||
|
||||
/* The file extension must match and the total filename length can't be the
|
||||
* length of the extension else we do not have a filename. */
|
||||
if (!strcmpend(filename, valid_extension) &&
|
||||
strlen(filename) != strlen(valid_extension)) {
|
||||
ret = 1;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse an authorized client from a string. The format of a client string
|
||||
* looks like (see rend-spec-v3.txt):
|
||||
*
|
||||
* <auth-type>:<key-type>:<base32-encoded-public-key>
|
||||
*
|
||||
* The <auth-type> can only be "descriptor".
|
||||
* The <key-type> can only be "x25519".
|
||||
*
|
||||
* Return the key on success, return NULL, otherwise. */
|
||||
static hs_service_authorized_client_t *
|
||||
parse_authorized_client(const char *client_key_str)
|
||||
{
|
||||
char *auth_type = NULL;
|
||||
char *key_type = NULL;
|
||||
char *pubkey_b32 = NULL;
|
||||
hs_service_authorized_client_t *client = NULL;
|
||||
smartlist_t *fields = smartlist_new();
|
||||
|
||||
tor_assert(client_key_str);
|
||||
|
||||
smartlist_split_string(fields, client_key_str, ":",
|
||||
SPLIT_SKIP_SPACE, 0);
|
||||
/* Wrong number of fields. */
|
||||
if (smartlist_len(fields) != 3) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
auth_type = smartlist_get(fields, 0);
|
||||
key_type = smartlist_get(fields, 1);
|
||||
pubkey_b32 = smartlist_get(fields, 2);
|
||||
|
||||
/* Currently, the only supported auth type is "descriptor" and the only
|
||||
* supported key type is "x25519". */
|
||||
if (strcmp(auth_type, "descriptor") || strcmp(key_type, "x25519")) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* We expect a specific length of the base32 encoded key so make sure we
|
||||
* have that so we don't successfully decode a value with a different length
|
||||
* and end up in trouble when copying the decoded key into a fixed length
|
||||
* buffer. */
|
||||
if (strlen(pubkey_b32) != BASE32_NOPAD_LEN(CURVE25519_PUBKEY_LEN)) {
|
||||
log_warn(LD_REND, "Client authorization encoded base32 public key "
|
||||
"length is invalid: %s", pubkey_b32);
|
||||
goto err;
|
||||
}
|
||||
|
||||
client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
|
||||
if (base32_decode((char *) client->client_pk.public_key,
|
||||
sizeof(client->client_pk.public_key),
|
||||
pubkey_b32, strlen(pubkey_b32)) < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Success. */
|
||||
goto done;
|
||||
|
||||
err:
|
||||
service_authorized_client_free(client);
|
||||
done:
|
||||
/* It is also a good idea to wipe the public key. */
|
||||
if (pubkey_b32) {
|
||||
memwipe(pubkey_b32, 0, strlen(pubkey_b32));
|
||||
}
|
||||
if (fields) {
|
||||
SMARTLIST_FOREACH(fields, char *, s, tor_free(s));
|
||||
smartlist_free(fields);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/* Load all the client public keys for the given service. Return 0 on
|
||||
* success else -1 on failure. */
|
||||
static int
|
||||
load_client_keys(hs_service_t *service)
|
||||
{
|
||||
int ret = -1;
|
||||
char *client_key_str = NULL;
|
||||
char *client_key_file_path = NULL;
|
||||
char *client_keys_dir_path = NULL;
|
||||
hs_service_config_t *config;
|
||||
smartlist_t *file_list = NULL;
|
||||
|
||||
tor_assert(service);
|
||||
|
||||
config = &service->config;
|
||||
|
||||
/* Before calling this function, we already call load_service_keys to make
|
||||
* sure that the directory exists with the right permission. So, if we
|
||||
* cannot create a client pubkey key directory, we consider it as a bug. */
|
||||
client_keys_dir_path = hs_path_from_filename(config->directory_path,
|
||||
dname_client_pubkeys);
|
||||
if (BUG(hs_check_service_private_dir(get_options()->User,
|
||||
client_keys_dir_path,
|
||||
config->dir_group_readable, 1) < 0)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* If the list of clients already exists, we must clear it first. */
|
||||
if (config->clients) {
|
||||
SMARTLIST_FOREACH(config->clients, hs_service_authorized_client_t *, p,
|
||||
service_authorized_client_free(p));
|
||||
smartlist_free(config->clients);
|
||||
}
|
||||
|
||||
config->clients = smartlist_new();
|
||||
|
||||
file_list = tor_listdir(client_keys_dir_path);
|
||||
if (file_list == NULL) {
|
||||
log_warn(LD_REND, "Client authorization directory %s can't be listed.",
|
||||
client_keys_dir_path);
|
||||
goto end;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(file_list, const char *, filename) {
|
||||
hs_service_authorized_client_t *client = NULL;
|
||||
|
||||
if (client_filename_is_valid(filename)) {
|
||||
/* Create a full path for a file. */
|
||||
client_key_file_path = hs_path_from_filename(client_keys_dir_path,
|
||||
filename);
|
||||
client_key_str = read_file_to_str(client_key_file_path, 0, NULL);
|
||||
/* Free immediately after using it. */
|
||||
tor_free(client_key_file_path);
|
||||
|
||||
/* If we cannot read the file, continue with the next file. */
|
||||
if (!client_key_str) {
|
||||
continue;
|
||||
}
|
||||
|
||||
client = parse_authorized_client(client_key_str);
|
||||
/* Free immediately after using it. */
|
||||
tor_free(client_key_str);
|
||||
|
||||
if (client) {
|
||||
smartlist_add(config->clients, client);
|
||||
}
|
||||
}
|
||||
|
||||
} SMARTLIST_FOREACH_END(filename);
|
||||
|
||||
/* If the number of clients is greater than zero, set the flag to be true. */
|
||||
if (smartlist_len(config->clients) > 0) {
|
||||
config->is_client_auth_enabled = 1;
|
||||
}
|
||||
|
||||
/* Success. */
|
||||
ret = 0;
|
||||
end:
|
||||
if (client_key_str) {
|
||||
memwipe(client_key_str, 0, strlen(client_key_str));
|
||||
}
|
||||
if (file_list) {
|
||||
SMARTLIST_FOREACH(file_list, char *, s, tor_free(s));
|
||||
smartlist_free(file_list);
|
||||
}
|
||||
tor_free(client_key_str);
|
||||
tor_free(client_key_file_path);
|
||||
tor_free(client_keys_dir_path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
service_authorized_client_free_(hs_service_authorized_client_t *client)
|
||||
{
|
||||
if (!client) {
|
||||
return;
|
||||
}
|
||||
memwipe(&client->client_pk, 0, sizeof(client->client_pk));
|
||||
tor_free(client);
|
||||
}
|
||||
|
||||
/* Free a given service descriptor object and all key material is wiped. */
|
||||
STATIC void
|
||||
service_descriptor_free_(hs_service_descriptor_t *desc)
|
||||
|
@ -3281,6 +3487,7 @@ hs_service_lists_fnames_for_sandbox(smartlist_t *file_list,
|
|||
}
|
||||
service_add_fnames_to_list(service, file_list);
|
||||
smartlist_add_strdup(dir_list, service->config.directory_path);
|
||||
smartlist_add_strdup(dir_list, dname_client_pubkeys);
|
||||
} FOR_EACH_DESCRIPTOR_END;
|
||||
}
|
||||
|
||||
|
@ -3451,7 +3658,6 @@ hs_service_load_all_keys(void)
|
|||
if (load_service_keys(service) < 0) {
|
||||
goto err;
|
||||
}
|
||||
/* XXX: Load/Generate client authorization keys. (#20700) */
|
||||
} SMARTLIST_FOREACH_END(service);
|
||||
|
||||
/* Final step, the staging list contains service in a quiescent state that
|
||||
|
|
|
@ -148,6 +148,12 @@ typedef struct hs_service_keys_t {
|
|||
unsigned int is_identify_key_offline : 1;
|
||||
} hs_service_keys_t;
|
||||
|
||||
/** Service side configuration of client authorization. */
|
||||
typedef struct hs_service_authorized_client_t {
|
||||
/* The client auth public key used to encrypt the descriptor cookie. */
|
||||
curve25519_public_key_t client_pk;
|
||||
} hs_service_authorized_client_t;
|
||||
|
||||
/* Service configuration. The following are set from the torrc options either
|
||||
* set by the configuration file or by the control port. Nothing else should
|
||||
* change those values. */
|
||||
|
@ -176,6 +182,13 @@ typedef struct hs_service_config_t {
|
|||
* HiddenServiceNumIntroductionPoints option. */
|
||||
unsigned int num_intro_points;
|
||||
|
||||
/* True iff the client auth is enabled. */
|
||||
unsigned int is_client_auth_enabled : 1;
|
||||
|
||||
/* List of hs_service_authorized_client_t's of clients that may access this
|
||||
* service. Specified by HiddenServiceAuthorizeClient option. */
|
||||
smartlist_t *clients;
|
||||
|
||||
/* True iff we allow request made on unknown ports. Specified by
|
||||
* HiddenServiceAllowUnknownPorts option. */
|
||||
unsigned int allow_unknown_ports : 1;
|
||||
|
@ -356,6 +369,13 @@ STATIC void service_descriptor_free_(hs_service_descriptor_t *desc);
|
|||
#define service_descriptor_free(d) \
|
||||
FREE_AND_NULL(hs_service_descriptor_t, \
|
||||
service_descriptor_free_, (d))
|
||||
|
||||
STATIC void
|
||||
service_authorized_client_free_(hs_service_authorized_client_t *client);
|
||||
#define service_authorized_client_free(c) \
|
||||
FREE_AND_NULL(hs_service_authorized_client_t, \
|
||||
service_authorized_client_free_, (c))
|
||||
|
||||
STATIC int
|
||||
write_address_to_file(const hs_service_t *service, const char *fname_);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue