[tor-commits] [tor/master] control-port: Implement ONION_CLIENT_AUTH_ADD.

dgoulet at torproject.org dgoulet at torproject.org
Tue Nov 19 14:32:29 UTC 2019


commit 00fdaaee1e3cdfe40230a866c497d3648c43940c
Author: George Kadianakis <desnacked at riseup.net>
Date:   Thu May 30 15:58:20 2019 +0300

    control-port: Implement ONION_CLIENT_AUTH_ADD.
---
 src/core/include.am               |   2 +
 src/feature/control/control_cmd.c |   3 +
 src/feature/control/control_hs.c  | 161 ++++++++++++++++++++++++++++++++++++++
 src/feature/control/control_hs.h  |  24 ++++++
 src/feature/hs/hs_client.c        |  42 ++++++++--
 src/feature/hs/hs_client.h        |  31 +++++++-
 src/test/test_hs_control.c        |   9 +++
 7 files changed, 264 insertions(+), 8 deletions(-)

diff --git a/src/core/include.am b/src/core/include.am
index 868e3c49d..83230fb3c 100644
--- a/src/core/include.am
+++ b/src/core/include.am
@@ -83,6 +83,7 @@ LIBTOR_APP_A_SOURCES = 				\
 	src/feature/control/control_auth.c	\
 	src/feature/control/control_bootstrap.c	\
 	src/feature/control/control_cmd.c	\
+	src/feature/control/control_hs.c	\
 	src/feature/control/control_events.c	\
 	src/feature/control/control_fmt.c	\
 	src/feature/control/control_getinfo.c	\
@@ -330,6 +331,7 @@ noinst_HEADERS +=					\
 	src/feature/control/control.h			\
 	src/feature/control/control_auth.h		\
 	src/feature/control/control_cmd.h		\
+	src/feature/control/control_hs.h		\
 	src/feature/control/control_cmd_args_st.h	\
 	src/feature/control/control_connection_st.h	\
 	src/feature/control/control_events.h		\
diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c
index e1a8292c9..3c16722e1 100644
--- a/src/feature/control/control_cmd.c
+++ b/src/feature/control/control_cmd.c
@@ -26,6 +26,7 @@
 #include "feature/control/control.h"
 #include "feature/control/control_auth.h"
 #include "feature/control/control_cmd.h"
+#include "feature/control/control_hs.h"
 #include "feature/control/control_events.h"
 #include "feature/control/control_getinfo.h"
 #include "feature/control/control_proto.h"
@@ -1970,6 +1971,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
     decoded_key->v2 = pk;
     *hs_version = HS_VERSION_TWO;
   } else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
+    /* parsing of private ed25519 key */
     /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
     ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
     if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
@@ -2317,6 +2319,7 @@ static const control_cmd_def_t CONTROL_COMMANDS[] =
   MULTLINE(hspost, 0),
   ONE_LINE(add_onion, CMD_FL_WIPE),
   ONE_LINE(del_onion, CMD_FL_WIPE),
+  ONE_LINE(onion_client_auth_add, CMD_FL_WIPE),
 };
 
 /**
diff --git a/src/feature/control/control_hs.c b/src/feature/control/control_hs.c
new file mode 100644
index 000000000..5db8a0f00
--- /dev/null
+++ b/src/feature/control/control_hs.c
@@ -0,0 +1,161 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_hs.c
+ *
+ * \brief Implement commands for Tor's control-socket interface that are
+ *        related to onion services.
+ **/
+
+#include "core/or/or.h"
+#include "feature/control/control_cmd.h"
+#include "feature/control/control_hs.h"
+#include "feature/control/control_proto.h"
+#include "feature/hs/hs_client.h"
+#include "lib/encoding/confline.h"
+
+#include "feature/control/control_cmd_args_st.h"
+
+/** Parse the 'KeyType ":" PrivateKey' from <b>client_privkey_str</b> and store
+ *  it into <b>privkey</b>. Use <b>conn</b> to output any errors if needed.
+ *
+ *  Return 0 if all went well, -1 otherwise. */
+static int
+parse_private_key_from_control_port(const char *client_privkey_str,
+                                    curve25519_secret_key_t *privkey,
+                                    control_connection_t *conn)
+{
+  int retval = -1;
+  smartlist_t *key_args = smartlist_new();
+
+  tor_assert(privkey);
+
+  smartlist_split_string(key_args, client_privkey_str, ":",
+                         SPLIT_IGNORE_BLANK, 0);
+  if (smartlist_len(key_args) != 2) {
+    control_printf_endreply(conn, 512, "Invalid key type/blob");
+    goto err;
+  }
+
+  const char *key_type = smartlist_get(key_args, 0);
+  const char *key_blob = smartlist_get(key_args, 1);
+
+  if (strcasecmp(key_type, "x25519")) {
+    control_printf_endreply(conn, 552,
+                            "Unrecognized key type \"%s\"", key_type);
+    goto err;
+  }
+
+  if (base64_decode((char*)privkey->secret_key, sizeof(privkey->secret_key),
+                    key_blob,
+                   strlen(key_blob)) != sizeof(privkey->secret_key)) {
+    control_printf_endreply(conn, 512, "Failed to decode ED25519-V3 key");
+    goto err;
+  }
+
+  retval = 0;
+
+ err:
+  SMARTLIST_FOREACH(key_args, char *, c, tor_free(c));
+  smartlist_free(key_args);
+  return retval;
+}
+
+/** Syntax details for ONION_CLIENT_AUTH_ADD */
+const control_cmd_syntax_t onion_client_auth_add_syntax = {
+  .max_args = 2,
+  .accept_keywords = true,
+};
+
+/** Called when we get an ONION_CLIENT_AUTH_ADD command; parse the body, and
+ *  register the new client-side client auth credentials:
+ *  "ONION_CLIENT_AUTH_ADD" SP HSAddress
+ *                          SP KeyType ":" PrivateKeyBlob
+ *                          [SP "ClientName=" Nickname]
+ *                          [SP "Type=" TYPE] CRLF
+ */
+int
+handle_control_onion_client_auth_add(control_connection_t *conn,
+                                     const control_cmd_args_t *args)
+{
+  int retval = -1;
+  smartlist_t *flags = smartlist_new();
+  hs_client_service_authorization_t *creds = NULL;
+
+  tor_assert(args);
+
+  int argc = smartlist_len(args->args);
+  /* We need at least 'HSAddress' and 'PrivateKeyBlob' */
+  if (argc < 2) {
+    control_printf_endreply(conn, 512,
+                            "Incomplete ONION_CLIENT_AUTH_ADD command");
+    goto err;
+  }
+
+  creds = tor_malloc_zero(sizeof(hs_client_service_authorization_t));
+
+  const char *hsaddress = smartlist_get(args->args, 0);
+  if (!hs_address_is_valid(hsaddress)) {
+    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"",hsaddress);
+    goto err;
+  }
+  strlcpy(creds->onion_address, hsaddress, sizeof(creds->onion_address));
+
+  /* Parse the client private key */
+  const char *client_privkey = smartlist_get(args->args, 1);
+  if (parse_private_key_from_control_port(client_privkey,
+                                          &creds->enc_seckey, conn) < 0) {
+    goto err;
+  }
+
+  /* Now let's parse the remaining arguments (variable size) */
+  for (const config_line_t *line = args->kwargs; line; line = line->next) {
+    if (!strcasecmp(line->key, "ClientName")) {
+      /* XXX apply length restriction? */
+      creds->nickname = tor_strdup(line->value);
+
+    } else if (!strcasecmpstart(line->key, "Flags")) {
+      smartlist_split_string(flags, line->value, ",", SPLIT_IGNORE_BLANK, 0);
+      if (smartlist_len(flags) < 1) {
+        control_write_endreply(conn, 512, "Invalid 'Flags' argument");
+        goto err;
+      }
+      SMARTLIST_FOREACH_BEGIN(flags, const char *, flag) {
+        if (!strcasecmp(flag, "Permanent")) {
+          creds->flags |= CLIENT_AUTH_FLAG_IS_PERMANENT;
+        } else {
+          control_printf_endreply(conn, 512, "Invalid 'Flags' argument: %s",
+                                  escaped(flag));
+          goto err;
+        }
+      } SMARTLIST_FOREACH_END(flag);
+    }
+  }
+
+  hs_client_register_auth_status_t register_status;
+  /* Register the credential (register func takes ownership of cred.) */
+  register_status = hs_client_register_auth_credentials(creds);
+  if (BUG(register_status == REGISTER_FAIL_BAD_ADDRESS)) {
+    /* It's a bug because the service addr has already been validated above */
+    control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", hsaddress);
+  } else if (register_status == REGISTER_FAIL_ALREADY_EXISTS) {
+    control_printf_endreply(conn, 551, "Client already exists");
+  } else if (register_status == REGISTER_SUCCESS) {
+    control_printf_endreply(conn, 250, "OK");
+  } else {
+    tor_assert_nonfatal_unreached();
+  }
+
+  retval = 0;
+  goto done;
+
+ err:
+  client_service_authorization_free(creds);
+
+ done:
+  SMARTLIST_FOREACH(flags, char *, s, tor_free(s));
+  smartlist_free(flags);
+  return retval;
+}
diff --git a/src/feature/control/control_hs.h b/src/feature/control/control_hs.h
new file mode 100644
index 000000000..1fcd7de36
--- /dev/null
+++ b/src/feature/control/control_hs.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2019, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file control_hs.c
+ *
+ * \brief Header file for control_hs.c.
+ **/
+
+#ifndef TOR_CONTROL_HS_H
+#define TOR_CONTROL_HS_H
+
+struct control_cmd_syntax_t;
+
+extern const char *onion_client_auth_add_keywords[];
+extern const struct control_cmd_syntax_t onion_client_auth_add_syntax;
+
+int
+handle_control_onion_client_auth_add(control_connection_t *conn,
+                                     const control_cmd_args_t *args);
+
+#endif
+
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 0d7c48dd8..7b981908d 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -1445,6 +1445,32 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason,
                                 NULL);
 }
 
+/** Register the credential <b>creds</b> as part of the client auth subsystem.
+ *
+ * Takes ownership of <b>creds</b>.
+ **/
+hs_client_register_auth_status_t
+hs_client_register_auth_credentials(hs_client_service_authorization_t *creds)
+{
+  ed25519_public_key_t service_identity_pk;
+
+  tor_assert(creds);
+
+  if (hs_parse_address(creds->onion_address, &service_identity_pk,
+                       NULL, NULL) < 0) {
+    client_service_authorization_free(creds);
+    return REGISTER_FAIL_BAD_ADDRESS;
+  }
+
+  if (digest256map_get(client_auths, service_identity_pk.pubkey)) {
+    client_service_authorization_free(creds);
+    return REGISTER_FAIL_ALREADY_EXISTS;
+  }
+
+  digest256map_set(client_auths, service_identity_pk.pubkey, creds);
+  return REGISTER_SUCCESS;
+}
+
 /* ========== */
 /* Public API */
 /* ========== */
@@ -1669,16 +1695,18 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
   return -1;
 }
 
-#define client_service_authorization_free(auth)                      \
-  FREE_AND_NULL(hs_client_service_authorization_t,                   \
-                client_service_authorization_free_, (auth))
-
-static void
+void
 client_service_authorization_free_(hs_client_service_authorization_t *auth)
 {
-  if (auth) {
-    memwipe(auth, 0, sizeof(*auth));
+  if (!auth) {
+    return;
+  }
+
+  if (auth->nickname) {
+    tor_free(auth->nickname);
   }
+
+  memwipe(auth, 0, sizeof(*auth));
   tor_free(auth);
 }
 
diff --git a/src/feature/hs/hs_client.h b/src/feature/hs/hs_client.h
index 699ce6051..ea726e237 100644
--- a/src/feature/hs/hs_client.h
+++ b/src/feature/hs/hs_client.h
@@ -31,7 +31,20 @@ typedef enum {
   HS_CLIENT_FETCH_PENDING      = 5,
 } hs_client_fetch_status_t;
 
-/** Client-side configuration of authorization for a service. */
+/* Status code of client auth credential registration */
+typedef enum {
+  /* We successfuly registered these credentials */
+  REGISTER_SUCCESS,
+  /* We failed to register these credentials, because they already exist. */
+  REGISTER_FAIL_ALREADY_EXISTS,
+  /* We failed to register these credentials, because of a bad HS address. */
+  REGISTER_FAIL_BAD_ADDRESS,
+} hs_client_register_auth_status_t;
+
+/** Flag to set when a client auth is permanent (saved on disk). */
+#define CLIENT_AUTH_FLAG_IS_PERMANENT (1<<0)
+
+/** Client-side configuration of client authorization */
 typedef struct hs_client_service_authorization_t {
   /** An curve25519 secret key used to compute decryption keys that
    * allow the client to decrypt the hidden service descriptor. */
@@ -39,8 +52,24 @@ typedef struct hs_client_service_authorization_t {
 
   /** An onion address that is used to connect to the onion service. */
   char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1];
+
+  /* An optional nickname for this client */
+  char *nickname;
+
+  /* Optional flags for this client. */
+  int flags;
 } hs_client_service_authorization_t;
 
+hs_client_register_auth_status_t
+hs_client_register_auth_credentials(hs_client_service_authorization_t *creds);
+
+#define client_service_authorization_free(auth)                      \
+  FREE_AND_NULL(hs_client_service_authorization_t,                   \
+                client_service_authorization_free_, (auth))
+
+void
+client_service_authorization_free_(hs_client_service_authorization_t *auth);
+
 void hs_client_note_connection_attempt_succeeded(
                                        const edge_connection_t *conn);
 
diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c
index 7cedc987b..3eadd4966 100644
--- a/src/test/test_hs_control.c
+++ b/src/test/test_hs_control.c
@@ -187,9 +187,18 @@ test_hs_desc_event(void *arg)
   tor_free(expected_msg);
 }
 
+static void
+test_hs_control_onion_client_auth_add(void *arg)
+{
+  (void) arg;
+}
+
 struct testcase_t hs_control_tests[] = {
   { "hs_desc_event", test_hs_desc_event, TT_FORK,
     NULL, NULL },
+  { "hs_control_onion_client_auth_add",
+    test_hs_control_onion_client_auth_add, TT_FORK,
+    NULL, NULL },
 
   END_OF_TESTCASES
 };





More information about the tor-commits mailing list