mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-14 07:03:44 +01:00
control-port: Implement ONION_CLIENT_AUTH_ADD.
This commit is contained in:
parent
d28b6792cb
commit
00fdaaee1e
@ -83,6 +83,7 @@ LIBTOR_APP_A_SOURCES = \
|
|||||||
src/feature/control/control_auth.c \
|
src/feature/control/control_auth.c \
|
||||||
src/feature/control/control_bootstrap.c \
|
src/feature/control/control_bootstrap.c \
|
||||||
src/feature/control/control_cmd.c \
|
src/feature/control/control_cmd.c \
|
||||||
|
src/feature/control/control_hs.c \
|
||||||
src/feature/control/control_events.c \
|
src/feature/control/control_events.c \
|
||||||
src/feature/control/control_fmt.c \
|
src/feature/control/control_fmt.c \
|
||||||
src/feature/control/control_getinfo.c \
|
src/feature/control/control_getinfo.c \
|
||||||
@ -330,6 +331,7 @@ noinst_HEADERS += \
|
|||||||
src/feature/control/control.h \
|
src/feature/control/control.h \
|
||||||
src/feature/control/control_auth.h \
|
src/feature/control/control_auth.h \
|
||||||
src/feature/control/control_cmd.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_cmd_args_st.h \
|
||||||
src/feature/control/control_connection_st.h \
|
src/feature/control/control_connection_st.h \
|
||||||
src/feature/control/control_events.h \
|
src/feature/control/control_events.h \
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "feature/control/control.h"
|
#include "feature/control/control.h"
|
||||||
#include "feature/control/control_auth.h"
|
#include "feature/control/control_auth.h"
|
||||||
#include "feature/control/control_cmd.h"
|
#include "feature/control/control_cmd.h"
|
||||||
|
#include "feature/control/control_hs.h"
|
||||||
#include "feature/control/control_events.h"
|
#include "feature/control/control_events.h"
|
||||||
#include "feature/control/control_getinfo.h"
|
#include "feature/control/control_getinfo.h"
|
||||||
#include "feature/control/control_proto.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;
|
decoded_key->v2 = pk;
|
||||||
*hs_version = HS_VERSION_TWO;
|
*hs_version = HS_VERSION_TWO;
|
||||||
} else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
|
} 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-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */
|
||||||
ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
|
ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
|
||||||
if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob,
|
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),
|
MULTLINE(hspost, 0),
|
||||||
ONE_LINE(add_onion, CMD_FL_WIPE),
|
ONE_LINE(add_onion, CMD_FL_WIPE),
|
||||||
ONE_LINE(del_onion, CMD_FL_WIPE),
|
ONE_LINE(del_onion, CMD_FL_WIPE),
|
||||||
|
ONE_LINE(onion_client_auth_add, CMD_FL_WIPE),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
161
src/feature/control/control_hs.c
Normal file
161
src/feature/control/control_hs.c
Normal file
@ -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;
|
||||||
|
}
|
24
src/feature/control/control_hs.h
Normal file
24
src/feature/control/control_hs.h
Normal file
@ -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
|
||||||
|
|
@ -1445,6 +1445,32 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason,
|
|||||||
NULL);
|
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 */
|
/* Public API */
|
||||||
/* ========== */
|
/* ========== */
|
||||||
@ -1669,16 +1695,18 @@ hs_client_receive_rendezvous_acked(origin_circuit_t *circ,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define client_service_authorization_free(auth) \
|
void
|
||||||
FREE_AND_NULL(hs_client_service_authorization_t, \
|
|
||||||
client_service_authorization_free_, (auth))
|
|
||||||
|
|
||||||
static void
|
|
||||||
client_service_authorization_free_(hs_client_service_authorization_t *auth)
|
client_service_authorization_free_(hs_client_service_authorization_t *auth)
|
||||||
{
|
{
|
||||||
if (auth) {
|
if (!auth) {
|
||||||
memwipe(auth, 0, sizeof(*auth));
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (auth->nickname) {
|
||||||
|
tor_free(auth->nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
memwipe(auth, 0, sizeof(*auth));
|
||||||
tor_free(auth);
|
tor_free(auth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,20 @@ typedef enum {
|
|||||||
HS_CLIENT_FETCH_PENDING = 5,
|
HS_CLIENT_FETCH_PENDING = 5,
|
||||||
} hs_client_fetch_status_t;
|
} 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 {
|
typedef struct hs_client_service_authorization_t {
|
||||||
/** An curve25519 secret key used to compute decryption keys that
|
/** An curve25519 secret key used to compute decryption keys that
|
||||||
* allow the client to decrypt the hidden service descriptor. */
|
* 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. */
|
/** An onion address that is used to connect to the onion service. */
|
||||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32+1];
|
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_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(
|
void hs_client_note_connection_attempt_succeeded(
|
||||||
const edge_connection_t *conn);
|
const edge_connection_t *conn);
|
||||||
|
|
||||||
|
@ -187,9 +187,18 @@ test_hs_desc_event(void *arg)
|
|||||||
tor_free(expected_msg);
|
tor_free(expected_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_hs_control_onion_client_auth_add(void *arg)
|
||||||
|
{
|
||||||
|
(void) arg;
|
||||||
|
}
|
||||||
|
|
||||||
struct testcase_t hs_control_tests[] = {
|
struct testcase_t hs_control_tests[] = {
|
||||||
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
|
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
|
||||||
NULL, NULL },
|
NULL, NULL },
|
||||||
|
{ "hs_control_onion_client_auth_add",
|
||||||
|
test_hs_control_onion_client_auth_add, TT_FORK,
|
||||||
|
NULL, NULL },
|
||||||
|
|
||||||
END_OF_TESTCASES
|
END_OF_TESTCASES
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user