mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-24 12:23:32 +01:00
hs-v3: Add ephemeral service support
The functions are now used by the ADD_ONION/DEL_ONION control port command as well. This commits makes them fully functionnal with hidden service v3. Part of #20699 Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
5d180309ea
commit
f0e3331f3c
@ -4447,19 +4447,14 @@ add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs,
|
||||
|
||||
switch (hs_version) {
|
||||
case HS_VERSION_TWO:
|
||||
{
|
||||
ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
|
||||
max_streams_close_circuit, auth_type,
|
||||
auth_clients, address_out);
|
||||
break;
|
||||
}
|
||||
case HS_VERSION_THREE:
|
||||
{
|
||||
/* XXX: Not implemented yet. */
|
||||
*address_out = tor_strdup("this is a v3 address");
|
||||
ret = RSAE_OKAY;
|
||||
ret = hs_service_add_ephemeral(pk, port_cfgs, max_streams,
|
||||
max_streams_close_circuit, address_out);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
tor_assert_unreached();
|
||||
}
|
||||
@ -4971,6 +4966,7 @@ handle_control_del_onion(control_connection_t *conn,
|
||||
uint32_t len,
|
||||
const char *body)
|
||||
{
|
||||
int hs_version = 0;
|
||||
smartlist_t *args;
|
||||
(void) len; /* body is nul-terminated; it's safe to ignore the length */
|
||||
args = getargs_helper("DEL_ONION", conn, body, 1, 1);
|
||||
@ -4978,7 +4974,11 @@ handle_control_del_onion(control_connection_t *conn,
|
||||
return 0;
|
||||
|
||||
const char *service_id = smartlist_get(args, 0);
|
||||
if (!rend_valid_v2_service_id(service_id)) {
|
||||
if (rend_valid_v2_service_id(service_id)) {
|
||||
hs_version = HS_VERSION_TWO;
|
||||
} else if (hs_address_is_valid(service_id)) {
|
||||
hs_version = HS_VERSION_THREE;
|
||||
} else {
|
||||
connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
|
||||
goto out;
|
||||
}
|
||||
@ -5005,8 +5005,20 @@ handle_control_del_onion(control_connection_t *conn,
|
||||
if (onion_services == NULL) {
|
||||
connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n");
|
||||
} else {
|
||||
int ret = rend_service_del_ephemeral(service_id);
|
||||
if (ret) {
|
||||
int ret = -1;
|
||||
switch (hs_version) {
|
||||
case HS_VERSION_TWO:
|
||||
ret = rend_service_del_ephemeral(service_id);
|
||||
break;
|
||||
case HS_VERSION_THREE:
|
||||
ret = hs_service_del_ephemeral(service_id);
|
||||
break;
|
||||
default:
|
||||
/* The ret value will be -1 thus hitting the warning below. This should
|
||||
* never happen because of the check at the start of the function. */
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* This should *NEVER* fail, since the service is on either the
|
||||
* per-control connection list, or the global one.
|
||||
*/
|
||||
@ -5076,9 +5088,16 @@ connection_control_closed(control_connection_t *conn)
|
||||
* The list and it's contents are scrubbed/freed in connection_free_.
|
||||
*/
|
||||
if (conn->ephemeral_onion_services) {
|
||||
SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, {
|
||||
rend_service_del_ephemeral(cp);
|
||||
});
|
||||
SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) {
|
||||
if (rend_valid_v2_service_id(cp)) {
|
||||
rend_service_del_ephemeral(cp);
|
||||
} else if (hs_address_is_valid(cp)) {
|
||||
hs_service_del_ephemeral(cp);
|
||||
} else {
|
||||
/* An invalid .onion in our list should NEVER happen */
|
||||
tor_fragile_assert();
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(cp);
|
||||
}
|
||||
|
||||
if (conn->is_owning_control_connection) {
|
||||
|
@ -2900,6 +2900,132 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
|
||||
/* Public API */
|
||||
/* ========== */
|
||||
|
||||
/* Add the ephemeral service using the secret key sk and ports. Both max
|
||||
* streams parameter will be set in the newly created service.
|
||||
*
|
||||
* Ownership of sk and ports is passed to this routine. Regardless of
|
||||
* success/failure, callers should not touch these values after calling this
|
||||
* routine, and may assume that correct cleanup has been done on failure.
|
||||
*
|
||||
* Return an appropriate hs_service_add_ephemeral_status_t. */
|
||||
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)
|
||||
{
|
||||
hs_service_add_ephemeral_status_t ret;
|
||||
hs_service_t *service = NULL;
|
||||
|
||||
tor_assert(sk);
|
||||
tor_assert(ports);
|
||||
tor_assert(address_out);
|
||||
|
||||
service = hs_service_new(get_options());
|
||||
|
||||
/* Setup the service configuration with specifics. A default service is
|
||||
* HS_VERSION_TWO so explicitely set it. */
|
||||
service->config.version = HS_VERSION_THREE;
|
||||
service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit;
|
||||
service->config.max_streams_close_circuit = !!max_streams_close_circuit;
|
||||
service->config.is_ephemeral = 1;
|
||||
smartlist_free(service->config.ports);
|
||||
service->config.ports = ports;
|
||||
|
||||
/* Handle the keys. */
|
||||
memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk));
|
||||
if (ed25519_public_key_generate(&service->keys.identity_pk,
|
||||
&service->keys.identity_sk) < 0) {
|
||||
log_warn(LD_CONFIG, "Unable to generate ed25519 public key"
|
||||
"for v3 service.");
|
||||
ret = RSAE_BADPRIVKEY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Make sure we have at least one port. */
|
||||
if (smartlist_len(service->config.ports) == 0) {
|
||||
log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified "
|
||||
"for v3 service.");
|
||||
ret = RSAE_BADVIRTPORT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* The only way the registration can fail is if the service public key
|
||||
* already exists. */
|
||||
if (BUG(register_service(hs_service_map, service) < 0)) {
|
||||
log_warn(LD_CONFIG, "Onion Service private key collides with an "
|
||||
"existing v3 service.");
|
||||
ret = RSAE_ADDREXISTS;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Last step is to build the onion address. */
|
||||
hs_build_address(&service->keys.identity_pk,
|
||||
(uint8_t) service->config.version,
|
||||
service->onion_address);
|
||||
*address_out = tor_strdup(service->onion_address);
|
||||
|
||||
log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s",
|
||||
safe_str_client(service->onion_address));
|
||||
ret = RSAE_OKAY;
|
||||
goto end;
|
||||
|
||||
err:
|
||||
hs_service_free(service);
|
||||
|
||||
end:
|
||||
memwipe(sk, 0, sizeof(ed25519_secret_key_t));
|
||||
tor_free(sk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For the given onion address, delete the ephemeral service. Return 0 on
|
||||
* success else -1 on error. */
|
||||
int
|
||||
hs_service_del_ephemeral(const char *address)
|
||||
{
|
||||
uint8_t version;
|
||||
ed25519_public_key_t pk;
|
||||
hs_service_t *service = NULL;
|
||||
|
||||
tor_assert(address);
|
||||
|
||||
if (hs_parse_address(address, &pk, NULL, &version) < 0) {
|
||||
log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (version != HS_VERSION_THREE) {
|
||||
log_warn(LD_CONFIG, "Requested version of onion address for removal "
|
||||
"is not supported.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
service = find_service(hs_service_map, &pk);
|
||||
if (service == NULL) {
|
||||
log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for "
|
||||
"removal.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!service->config.is_ephemeral) {
|
||||
log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for "
|
||||
"removal.");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Close circuits, remove from map and finally free. */
|
||||
close_service_circuits(service);
|
||||
remove_service(hs_service_map, service);
|
||||
hs_service_free(service);
|
||||
|
||||
log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s",
|
||||
safe_str_client(address));
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Using the ed25519 public key pk, find a service for that key and return the
|
||||
* current encoded descriptor as a newly allocated string or NULL if not
|
||||
* found. This is used by the control port subsystem. */
|
||||
|
@ -273,6 +273,12 @@ void hs_service_intro_circ_has_closed(origin_circuit_t *circ);
|
||||
|
||||
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 hs_service_del_ephemeral(const char *address);
|
||||
|
||||
#ifdef HS_SERVICE_PRIVATE
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
@ -14,7 +14,81 @@
|
||||
#include "test_helpers.h"
|
||||
|
||||
static void
|
||||
test_add_onion_helper_keyarg(void *arg)
|
||||
test_add_onion_helper_keyarg_v3(void *arg)
|
||||
{
|
||||
int ret, hs_version;
|
||||
void *pk_ptr = NULL;
|
||||
char *key_new_blob = NULL;
|
||||
char *err_msg = NULL;
|
||||
const char *key_new_alg = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
/* Test explicit ED25519-V3 key generation. */
|
||||
ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg,
|
||||
&key_new_blob, &pk_ptr, &hs_version,
|
||||
&err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
|
||||
tt_assert(pk_ptr);
|
||||
tt_str_op(key_new_alg, OP_EQ, "ED25519-V3");
|
||||
tt_assert(key_new_blob);
|
||||
tt_ptr_op(err_msg, OP_EQ, NULL);
|
||||
tor_free(pk_ptr); pk_ptr = NULL;
|
||||
tor_free(key_new_blob);
|
||||
|
||||
/* Test discarding the private key. */
|
||||
ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg,
|
||||
&key_new_blob, &pk_ptr, &hs_version,
|
||||
&err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
|
||||
tt_assert(pk_ptr);
|
||||
tt_ptr_op(key_new_alg, OP_EQ, NULL);
|
||||
tt_ptr_op(key_new_blob, OP_EQ, NULL);
|
||||
tt_ptr_op(err_msg, OP_EQ, NULL);
|
||||
tor_free(pk_ptr); pk_ptr = NULL;
|
||||
tor_free(key_new_blob);
|
||||
|
||||
/* Test passing a key blob. */
|
||||
{
|
||||
/* The base64 key and hex key are the same. Hex key is 64 bytes long. The
|
||||
* sk has been generated randomly using python3. */
|
||||
const char *base64_sk =
|
||||
"a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW"
|
||||
"6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw==";
|
||||
const char *hex_sk =
|
||||
"\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc"
|
||||
"\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44"
|
||||
"\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82"
|
||||
"\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b";
|
||||
char *key_blob = NULL;
|
||||
|
||||
tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk);
|
||||
tt_assert(key_blob);
|
||||
ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg,
|
||||
&key_new_blob, &pk_ptr, &hs_version,
|
||||
&err_msg);
|
||||
tor_free(key_blob);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
|
||||
tt_assert(pk_ptr);
|
||||
tt_mem_op(pk_ptr, OP_EQ, hex_sk, 64);
|
||||
tt_ptr_op(key_new_alg, OP_EQ, NULL);
|
||||
tt_ptr_op(key_new_blob, OP_EQ, NULL);
|
||||
tt_ptr_op(err_msg, OP_EQ, NULL);
|
||||
tor_free(pk_ptr); pk_ptr = NULL;
|
||||
tor_free(key_new_blob);
|
||||
}
|
||||
|
||||
done:
|
||||
tor_free(pk_ptr);
|
||||
tor_free(key_new_blob);
|
||||
tor_free(err_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
test_add_onion_helper_keyarg_v2(void *arg)
|
||||
{
|
||||
int ret, hs_version;
|
||||
void *pk_ptr = NULL;
|
||||
@ -1386,7 +1460,10 @@ test_download_status_bridge(void *arg)
|
||||
}
|
||||
|
||||
struct testcase_t controller_tests[] = {
|
||||
{ "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL },
|
||||
{ "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0,
|
||||
NULL, NULL },
|
||||
{ "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0,
|
||||
NULL, NULL },
|
||||
{ "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL },
|
||||
{ "rend_service_parse_port_config", test_rend_service_parse_port_config, 0,
|
||||
NULL, NULL },
|
||||
|
Loading…
Reference in New Issue
Block a user