Commit 00fdaaee authored by George Kadianakis's avatar George Kadianakis
Browse files

control-port: Implement ONION_CLIENT_AUTH_ADD.

parent d28b6792
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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		\
+3 −0
Original line number Diff line number Diff line
@@ -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),
};

/**
+161 −0
Original line number Diff line number Diff line
/* 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;
}
+24 −0
Original line number Diff line number Diff line
/* 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
+35 −7
Original line number Diff line number Diff line
@@ -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);
}

Loading