diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/control.c | 45 | ||||
-rw-r--r-- | src/or/hs_service.c | 126 | ||||
-rw-r--r-- | src/or/hs_service.h | 6 |
3 files changed, 164 insertions, 13 deletions
diff --git a/src/or/control.c b/src/or/control.c index 19527acb3c..d9102894e5 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -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) { diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 349aa265d3..944400a0e5 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -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. */ diff --git a/src/or/hs_service.h b/src/or/hs_service.h index e78cc4c2b1..de04987c8b 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -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 |