[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor/master] hs-v3: Load all client auth keys to the service
commit b894b40e647b4839f33f3a57704cafe9e644230c
Author: Suphanat Chunhapanya <haxx.pop@xxxxxxxxx>
Date: Sat Aug 18 12:28:12 2018 +0700
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@xxxxxxxxxxxxxx>
---
src/feature/hs/hs_service.c | 208 +++++++++++++++++++++++++++++++++++++++++++-
src/feature/hs/hs_service.h | 20 +++++
2 files changed, 227 insertions(+), 1 deletion(-)
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index 30d01540f..d61b2809b 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -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
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index 17c654ecf..c64eb7763 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -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_);
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits