mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-30 23:53:32 +01:00
Add support for creating v3 onion services form the control port
This commit is contained in:
parent
d425dbf04a
commit
eacf528915
@ -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. */
|
||||
|
@ -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);
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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_);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user