[tor-commits] [tor/master] Add support for creating v3 onion services form the control port

asn at torproject.org asn at torproject.org
Mon Feb 8 11:35:16 UTC 2021


commit eacf52891587cd9201407831a3112376dad9cf4d
Author: Neel Chauhan <neel at neelc.org>
Date:   Sun Nov 15 12:56:14 2020 -0800

    Add support for creating v3 onion services form the control port
---
 src/core/or/or.h                     |  1 +
 src/feature/control/control_cmd.c    | 77 ++++++++++++++++++++++++++++++------
 src/feature/control/control_events.c |  3 ++
 src/feature/hs/hs_service.c          | 67 ++++++++++++++++++++++---------
 src/feature/hs/hs_service.h          | 18 +++++----
 5 files changed, 129 insertions(+), 37 deletions(-)

diff --git a/src/core/or/or.h b/src/core/or/or.h
index d80c41371e..646dbf2c3a 100644
--- a/src/core/or/or.h
+++ b/src/core/or/or.h
@@ -404,6 +404,7 @@ typedef enum rend_auth_type_t {
   REND_NO_AUTH      = 0,
   REND_BASIC_AUTH   = 1,
   REND_STEALTH_AUTH = 2,
+  REND_V3_AUTH      = 3,
 } rend_auth_type_t;
 
 /** Client-side configuration of authorization for a hidden service. */
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
index 5b75c24692..4b02b1c5c0 100644
--- a/src/feature/control/control_cmd.c
+++ b/src/feature/control/control_cmd.c
@@ -33,6 +33,7 @@
 #include "feature/control/control_getinfo.h"
 #include "feature/control/control_proto.h"
 #include "feature/hs/hs_control.h"
+#include "feature/hs/hs_service.h"
 #include "feature/nodelist/nodelist.h"
 #include "feature/nodelist/routerinfo.h"
 #include "feature/nodelist/routerlist.h"
@@ -1653,7 +1654,8 @@ add_onion_helper_add_service(int hs_version,
                              add_onion_secret_key_t *pk,
                              smartlist_t *port_cfgs, int max_streams,
                              int max_streams_close_circuit, int auth_type,
-                             smartlist_t *auth_clients, char **address_out)
+                             smartlist_t *auth_clients,
+                             smartlist_t *auth_clients_v3, char **address_out)
 {
   hs_service_add_ephemeral_status_t ret;
 
@@ -1669,7 +1671,8 @@ add_onion_helper_add_service(int hs_version,
     break;
   case HS_VERSION_THREE:
     ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
-                                   max_streams_close_circuit, address_out);
+                                   max_streams_close_circuit,
+                                   auth_clients_v3, address_out);
     break;
   default:
     tor_assert_unreached();
@@ -1693,7 +1696,7 @@ get_detached_onion_services(void)
 }
 
 static const char *add_onion_keywords[] = {
-   "Port", "Flags", "MaxStreams", "ClientAuth", NULL
+   "Port", "Flags", "MaxStreams", "ClientAuth", "ClientAuthV3", NULL
 };
 static const control_cmd_syntax_t add_onion_syntax = {
   .min_args = 1, .max_args = 1,
@@ -1714,6 +1717,8 @@ handle_control_add_onion(control_connection_t *conn,
   smartlist_t *port_cfgs = smartlist_new();
   smartlist_t *auth_clients = NULL;
   smartlist_t *auth_created_clients = NULL;
+  smartlist_t *auth_clients_v3 = NULL;
+  smartlist_t *auth_clients_v3_str = NULL;
   int discard_pk = 0;
   int detach = 0;
   int max_streams = 0;
@@ -1758,6 +1763,7 @@ handle_control_add_onion(control_connection_t *conn,
       static const char *detach_flag = "Detach";
       static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
       static const char *basicauth_flag = "BasicAuth";
+      static const char *v3auth_flag = "V3Auth";
       static const char *non_anonymous_flag = "NonAnonymous";
 
       smartlist_t *flags = smartlist_new();
@@ -1778,6 +1784,8 @@ handle_control_add_onion(control_connection_t *conn,
           max_streams_close_circuit = 1;
         } else if (!strcasecmp(flag, basicauth_flag)) {
           auth_type = REND_BASIC_AUTH;
+        } else if (!strcasecmp(flag, v3auth_flag)) {
+          auth_type = REND_V3_AUTH;
         } else if (!strcasecmp(flag, non_anonymous_flag)) {
           non_anonymous = 1;
         } else {
@@ -1821,6 +1829,20 @@ handle_control_add_onion(control_connection_t *conn,
       if (created) {
         smartlist_add(auth_created_clients, client);
       }
+    } else if (!strcasecmp(arg->key, "ClientAuthV3")) {
+      hs_service_authorized_client_t *client_v3 =
+                                       parse_authorized_client_key(arg->value);
+      if (!client_v3) {
+        goto out;
+      }
+
+      if (auth_clients_v3 == NULL) {
+        auth_clients_v3 = smartlist_new();
+        auth_clients_v3_str = smartlist_new();
+      }
+
+      smartlist_add(auth_clients_v3, client_v3);
+      smartlist_add(auth_clients_v3_str, tor_strdup(arg->value));
     } else {
       tor_assert_nonfatal_unreached();
       goto out;
@@ -1829,10 +1851,12 @@ handle_control_add_onion(control_connection_t *conn,
   if (smartlist_len(port_cfgs) == 0) {
     control_write_endreply(conn, 512, "Missing 'Port' argument");
     goto out;
-  } else if (auth_type == REND_NO_AUTH && auth_clients != NULL) {
+  } else if (auth_type == REND_NO_AUTH &&
+             (auth_clients != NULL && auth_clients_v3 != NULL)) {
     control_write_endreply(conn, 512, "No auth type specified");
     goto out;
-  } else if (auth_type != REND_NO_AUTH && auth_clients == NULL) {
+  } else if (auth_type != REND_NO_AUTH &&
+             (auth_clients == NULL && auth_clients_v3 == NULL)) {
     control_write_endreply(conn, 512, "No auth clients specified");
     goto out;
   } else if ((auth_type == REND_BASIC_AUTH &&
@@ -1841,6 +1865,15 @@ handle_control_add_onion(control_connection_t *conn,
               smartlist_len(auth_clients) > 16)) {
     control_write_endreply(conn, 512, "Too many auth clients");
     goto out;
+  } else if ((auth_type == REND_BASIC_AUTH ||
+              auth_type == REND_STEALTH_AUTH) && auth_clients_v3) {
+    control_write_endreply(conn, 512,
+                        "ClientAuthV3 does not support basic or stealth auth");
+    goto out;
+  } else if (auth_type == REND_V3_AUTH && auth_clients) {
+    control_write_endreply(conn, 512, "ClientAuth does not support v3 auth");
+    goto out;
+
   } else if (non_anonymous != rend_service_non_anonymous_mode_enabled(
                                                               get_options())) {
     /* If we failed, and the non-anonymous flag is set, Tor must be in
@@ -1869,12 +1902,16 @@ handle_control_add_onion(control_connection_t *conn,
     goto out;
   }
 
-  /* Hidden service version 3 don't have client authentication support so if
-   * ClientAuth was given, send back an error. */
+  /* We can't mix ClientAuth and Version 3 Onion Services, or ClientAuthV3 and
+   * Version 2. If that's the case, send back an error. */
   if (hs_version == HS_VERSION_THREE && auth_clients) {
     control_write_endreply(conn, 513, "ClientAuth not supported");
     goto out;
   }
+  if (hs_version == HS_VERSION_TWO && auth_clients_v3) {
+    control_write_endreply(conn, 513, "ClientAuthV3 not supported");
+    goto out;
+  }
 
   /* Create the HS, using private key pk, client authentication auth_type,
    * the list of auth_clients, and port config port_cfg.
@@ -1882,12 +1919,13 @@ handle_control_add_onion(control_connection_t *conn,
    * regardless of success/failure.
    */
   char *service_id = NULL;
-  int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs,
-                                         max_streams,
-                                         max_streams_close_circuit, auth_type,
-                                         auth_clients, &service_id);
+  int ret =
+      add_onion_helper_add_service(hs_version, &pk, port_cfgs, max_streams,
+                                   max_streams_close_circuit, auth_type,
+                                   auth_clients, auth_clients_v3, &service_id);
   port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
   auth_clients = NULL; /* so is auth_clients */
+  auth_clients_v3 = NULL; /* so is auth_clients_v3 */
   switch (ret) {
   case RSAE_OKAY:
   {
@@ -1919,6 +1957,11 @@ handle_control_add_onion(control_connection_t *conn,
         tor_free(encoded);
       });
     }
+    if (auth_clients_v3_str) {
+      SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str, {
+        control_printf_midreply(conn, 250, "ClientAuthV3=%s", client_str);
+      });
+    }
 
     send_control_done(conn);
     break;
@@ -1956,6 +1999,18 @@ handle_control_add_onion(control_connection_t *conn,
                       rend_authorized_client_free(ac));
     smartlist_free(auth_clients);
   }
+  if (auth_clients_v3) {
+    SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, ac,
+                      service_authorized_client_free(ac));
+    smartlist_free(auth_clients_v3);
+  }
+  if (auth_clients_v3_str) {
+    SMARTLIST_FOREACH(auth_clients_v3_str, char *, client_str,
+                      tor_free(client_str));
+    smartlist_free(auth_clients_v3_str);
+  }
+
+
   if (auth_created_clients) {
     // Do not free entries; they are the same as auth_clients
     smartlist_free(auth_created_clients);
diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c
index 0dd52659ec..c0ccb1eb26 100644
--- a/src/feature/control/control_events.c
+++ b/src/feature/control/control_events.c
@@ -1927,6 +1927,9 @@ rend_auth_type_to_string(rend_auth_type_t auth_type)
     case REND_STEALTH_AUTH:
       str = "STEALTH_AUTH";
       break;
+    case REND_V3_AUTH:
+      str = "REND_V3_AUTH";
+      break;
     default:
       str = "UNKNOWN";
   }
diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c
index fee999cac5..0d7441ada2 100644
--- a/src/feature/hs/hs_service.c
+++ b/src/feature/hs/hs_service.c
@@ -1115,6 +1115,43 @@ client_filename_is_valid(const char *filename)
   return ret;
 }
 
+/** Parse an base32-encoded authorized client from a string.
+ *
+ * Return the key on success, return NULL, otherwise. */
+hs_service_authorized_client_t *
+parse_authorized_client_key(const char *key_str)
+{
+  hs_service_authorized_client_t *client = NULL;
+
+  /* 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(key_str) != BASE32_NOPAD_LEN(CURVE25519_PUBKEY_LEN)) {
+    log_warn(LD_REND, "Client authorization encoded base32 public key "
+                      "length is invalid: %s", key_str);
+    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),
+                    key_str, strlen(key_str)) !=
+      sizeof(client->client_pk.public_key)) {
+    log_warn(LD_REND, "Client authorization public key cannot be decoded: %s",
+             key_str);
+    goto err;
+  }
+
+  return client;
+
+ err:
+  if (client != NULL) {
+    tor_free(client);
+  }
+  return NULL;
+}
+
 /** Parse an authorized client from a string. The format of a client string
  * looks like (see rend-spec-v3.txt):
  *
@@ -1161,23 +1198,7 @@ parse_authorized_client(const char *client_key_str)
     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)) !=
-      sizeof(client->client_pk.public_key)) {
-    log_warn(LD_REND, "Client authorization public key cannot be decoded: %s",
-             pubkey_b32);
+  if ((client = parse_authorized_client_key(pubkey_b32)) == NULL) {
     goto err;
   }
 
@@ -1301,7 +1322,7 @@ load_client_keys(hs_service_t *service)
 }
 
 /** Release all storage held in <b>client</b>. */
-STATIC void
+void
 service_authorized_client_free_(hs_service_authorized_client_t *client)
 {
   if (!client) {
@@ -3687,7 +3708,8 @@ hs_service_upload_desc_to_dir(const char *encoded_desc,
 hs_service_add_ephemeral_status_t
 hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
                          int max_streams_per_rdv_circuit,
-                         int max_streams_close_circuit, char **address_out)
+                         int max_streams_close_circuit,
+                         smartlist_t *auth_clients_v3, char **address_out)
 {
   hs_service_add_ephemeral_status_t ret;
   hs_service_t *service = NULL;
@@ -3731,6 +3753,13 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
     goto err;
   }
 
+  if (service->config.clients == NULL) {
+    service->config.clients = smartlist_new();
+  }
+  SMARTLIST_FOREACH(auth_clients_v3, hs_service_authorized_client_t *, c,
+    smartlist_add(service->config.clients, c));
+
+
   /* Build the onion address for logging purposes but also the control port
    * uses it for the HS_DESC event. */
   hs_build_address(&service->keys.identity_pk,
diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h
index ec0e83f2c2..4d49929127 100644
--- a/src/feature/hs/hs_service.h
+++ b/src/feature/hs/hs_service.h
@@ -372,7 +372,8 @@ char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk);
 hs_service_add_ephemeral_status_t
 hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports,
                          int max_streams_per_rdv_circuit,
-                         int max_streams_close_circuit, char **address_out);
+                         int max_streams_close_circuit,
+                         smartlist_t *auth_clients_v3, char **address_out);
 int hs_service_del_ephemeral(const char *address);
 
 /* Used outside of the HS subsystem by the control port command HSPOST. */
@@ -388,6 +389,15 @@ hs_service_exports_circuit_id(const ed25519_public_key_t *pk);
 void hs_service_dump_stats(int severity);
 void hs_service_circuit_cleanup_on_close(const circuit_t *circ);
 
+hs_service_authorized_client_t *
+parse_authorized_client_key(const char *key_str);
+
+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))
+
 #ifdef HS_SERVICE_PRIVATE
 
 #ifdef TOR_UNIT_TESTS
@@ -452,12 +462,6 @@ STATIC void service_descriptor_free_(hs_service_descriptor_t *desc);
   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