[tor-commits] [tor/master] hs-v3: Load all client auth keys to the service

nickm at torproject.org nickm at torproject.org
Fri Sep 7 19:06:18 UTC 2018


commit b894b40e647b4839f33f3a57704cafe9e644230c
Author: Suphanat Chunhapanya <haxx.pop at gmail.com>
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 at torproject.org>
---
 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_);
 





More information about the tor-commits mailing list