mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-20 02:09:24 +01:00
Merge branch 'dgoulet_ticket20699_033_01'
This commit is contained in:
commit
e8a6a6635b
14
changes/ticket20699
Normal file
14
changes/ticket20699
Normal file
@ -0,0 +1,14 @@
|
||||
o Major features (hidden service v3, control port):
|
||||
- Control port now supports command and events for hidden service v3. See
|
||||
proposal 284 for more information on what has been done exactly. Only
|
||||
the HSFETCH command hasn't been implemented at this stage because of a
|
||||
lack of use case with v3.
|
||||
|
||||
It is now possible to create ephemeral v3 services using the ADD_ONION
|
||||
command. Here is a summary of the events and commands that have been
|
||||
modified to support v3:
|
||||
|
||||
Events: HS_DESC, HS_DESC_CONTENT, CIRC and CIRC_MINOR The
|
||||
Commands: GETINFO, HSPOST, ADD_ONION and DEL_ONION.
|
||||
|
||||
This closes ticket 20699.
|
539
src/or/control.c
539
src/or/control.c
@ -58,7 +58,9 @@
|
||||
#include "entrynodes.h"
|
||||
#include "geoip.h"
|
||||
#include "hibernate.h"
|
||||
#include "hs_cache.h"
|
||||
#include "hs_common.h"
|
||||
#include "hs_control.h"
|
||||
#include "main.h"
|
||||
#include "microdesc.h"
|
||||
#include "networkstatus.h"
|
||||
@ -2014,36 +2016,89 @@ getinfo_helper_dir(control_connection_t *control_conn,
|
||||
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
|
||||
smartlist_free(sl);
|
||||
} else if (!strcmpstart(question, "hs/client/desc/id/")) {
|
||||
rend_cache_entry_t *e = NULL;
|
||||
hostname_type_t addr_type;
|
||||
|
||||
question += strlen("hs/client/desc/id/");
|
||||
if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
|
||||
if (rend_valid_v2_service_id(question)) {
|
||||
addr_type = ONION_V2_HOSTNAME;
|
||||
} else if (hs_address_is_valid(question)) {
|
||||
addr_type = ONION_V3_HOSTNAME;
|
||||
} else {
|
||||
*errmsg = "Invalid address";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!rend_cache_lookup_entry(question, -1, &e)) {
|
||||
/* Descriptor found in cache */
|
||||
*answer = tor_strdup(e->desc);
|
||||
if (addr_type == ONION_V2_HOSTNAME) {
|
||||
rend_cache_entry_t *e = NULL;
|
||||
if (!rend_cache_lookup_entry(question, -1, &e)) {
|
||||
/* Descriptor found in cache */
|
||||
*answer = tor_strdup(e->desc);
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
ed25519_public_key_t service_pk;
|
||||
const char *desc;
|
||||
|
||||
/* The check before this if/else makes sure of this. */
|
||||
tor_assert(addr_type == ONION_V3_HOSTNAME);
|
||||
|
||||
if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
|
||||
*errmsg = "Invalid v3 address";
|
||||
return -1;
|
||||
}
|
||||
|
||||
desc = hs_cache_lookup_encoded_as_client(&service_pk);
|
||||
if (desc) {
|
||||
*answer = tor_strdup(desc);
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else if (!strcmpstart(question, "hs/service/desc/id/")) {
|
||||
rend_cache_entry_t *e = NULL;
|
||||
hostname_type_t addr_type;
|
||||
|
||||
question += strlen("hs/service/desc/id/");
|
||||
if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
|
||||
if (rend_valid_v2_service_id(question)) {
|
||||
addr_type = ONION_V2_HOSTNAME;
|
||||
} else if (hs_address_is_valid(question)) {
|
||||
addr_type = ONION_V3_HOSTNAME;
|
||||
} else {
|
||||
*errmsg = "Invalid address";
|
||||
return -1;
|
||||
}
|
||||
rend_cache_entry_t *e = NULL;
|
||||
|
||||
if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
|
||||
/* Descriptor found in cache */
|
||||
*answer = tor_strdup(e->desc);
|
||||
if (addr_type == ONION_V2_HOSTNAME) {
|
||||
if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
|
||||
/* Descriptor found in cache */
|
||||
*answer = tor_strdup(e->desc);
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
ed25519_public_key_t service_pk;
|
||||
char *desc;
|
||||
|
||||
/* The check before this if/else makes sure of this. */
|
||||
tor_assert(addr_type == ONION_V3_HOSTNAME);
|
||||
|
||||
if (hs_parse_address(question, &service_pk, NULL, NULL) < 0) {
|
||||
*errmsg = "Invalid v3 address";
|
||||
return -1;
|
||||
}
|
||||
|
||||
desc = hs_service_lookup_current_desc(&service_pk);
|
||||
if (desc) {
|
||||
/* Newly allocated string, we have ownership. */
|
||||
*answer = desc;
|
||||
} else {
|
||||
*errmsg = "Not found in cache";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else if (!strcmpstart(question, "md/id/")) {
|
||||
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0);
|
||||
@ -2624,9 +2679,16 @@ circuit_describe_status_for_controller(origin_circuit_t *circ)
|
||||
}
|
||||
}
|
||||
|
||||
if (circ->rend_data != NULL) {
|
||||
smartlist_add_asprintf(descparts, "REND_QUERY=%s",
|
||||
rend_data_get_address(circ->rend_data));
|
||||
if (circ->rend_data != NULL || circ->hs_ident != NULL) {
|
||||
char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
const char *onion_address;
|
||||
if (circ->rend_data) {
|
||||
onion_address = rend_data_get_address(circ->rend_data);
|
||||
} else {
|
||||
hs_build_address(&circ->hs_ident->identity_pk, HS_VERSION_THREE, addr);
|
||||
onion_address = addr;
|
||||
}
|
||||
smartlist_add_asprintf(descparts, "REND_QUERY=%s", onion_address);
|
||||
}
|
||||
|
||||
{
|
||||
@ -4277,9 +4339,11 @@ handle_control_hspost(control_connection_t *conn,
|
||||
const char *body)
|
||||
{
|
||||
static const char *opt_server = "SERVER=";
|
||||
static const char *opt_hsaddress = "HSADDRESS=";
|
||||
smartlist_t *hs_dirs = NULL;
|
||||
const char *encoded_desc = body;
|
||||
size_t encoded_desc_len = len;
|
||||
const char *onion_address = NULL;
|
||||
|
||||
char *cp = memchr(body, '\n', len);
|
||||
if (cp == NULL) {
|
||||
@ -4309,15 +4373,16 @@ handle_control_hspost(control_connection_t *conn,
|
||||
server);
|
||||
goto done;
|
||||
}
|
||||
if (!node->rs->is_hs_dir) {
|
||||
connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
|
||||
"\r\n", server);
|
||||
goto done;
|
||||
}
|
||||
/* Valid server, add it to our local list. */
|
||||
if (!hs_dirs)
|
||||
hs_dirs = smartlist_new();
|
||||
smartlist_add(hs_dirs, node->rs);
|
||||
} else if (!strcasecmpstart(arg, opt_hsaddress)) {
|
||||
if (!hs_address_is_valid(arg)) {
|
||||
connection_printf_to_buf(conn, "512 Malformed onion address\r\n");
|
||||
goto done;
|
||||
}
|
||||
onion_address = arg;
|
||||
} else {
|
||||
connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
|
||||
arg);
|
||||
@ -4326,6 +4391,19 @@ handle_control_hspost(control_connection_t *conn,
|
||||
} SMARTLIST_FOREACH_END(arg);
|
||||
}
|
||||
|
||||
/* Handle the v3 case. */
|
||||
if (onion_address) {
|
||||
char *desc_str = NULL;
|
||||
read_escaped_data(encoded_desc, encoded_desc_len, &desc_str);
|
||||
if (hs_control_hspost_command(desc_str, onion_address, hs_dirs) < 0) {
|
||||
connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
|
||||
}
|
||||
tor_free(desc_str);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* From this point on, it is only v2. */
|
||||
|
||||
/* Read the dot encoded descriptor, and parse it. */
|
||||
rend_encoded_v2_service_descriptor_t *desc =
|
||||
tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
|
||||
@ -4370,6 +4448,52 @@ handle_control_hspost(control_connection_t *conn,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function for ADD_ONION that adds an ephemeral service depending on
|
||||
* the given hs_version.
|
||||
*
|
||||
* The secret key in pk depends on the hs_version. The ownership of the key
|
||||
* used in pk is given to the HS subsystem so the caller must stop accessing
|
||||
* it after.
|
||||
*
|
||||
* The port_cfgs is a list of service port. Ownership transfered to service.
|
||||
* The max_streams refers to the MaxStreams= key.
|
||||
* The max_streams_close_circuit refers to the MaxStreamsCloseCircuit key.
|
||||
* The auth_type is the authentication type of the clients in auth_clients.
|
||||
* The ownership of that list is transfered to the service.
|
||||
*
|
||||
* On success (RSAE_OKAY), the address_out points to a newly allocated string
|
||||
* containing the onion address without the .onion part. On error, address_out
|
||||
* is untouched. */
|
||||
static hs_service_add_ephemeral_status_t
|
||||
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)
|
||||
{
|
||||
hs_service_add_ephemeral_status_t ret;
|
||||
|
||||
tor_assert(pk);
|
||||
tor_assert(port_cfgs);
|
||||
tor_assert(address_out);
|
||||
|
||||
switch (hs_version) {
|
||||
case HS_VERSION_TWO:
|
||||
ret = rend_service_add_ephemeral(pk->v2, port_cfgs, max_streams,
|
||||
max_streams_close_circuit, auth_type,
|
||||
auth_clients, address_out);
|
||||
break;
|
||||
case HS_VERSION_THREE:
|
||||
ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams,
|
||||
max_streams_close_circuit, address_out);
|
||||
break;
|
||||
default:
|
||||
tor_assert_unreached();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Called when we get a ADD_ONION command; parse the body, and set up
|
||||
* the new ephemeral Onion Service. */
|
||||
static int
|
||||
@ -4551,15 +4675,15 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
}
|
||||
|
||||
/* Parse the "keytype:keyblob" argument. */
|
||||
crypto_pk_t *pk = NULL;
|
||||
int hs_version = 0;
|
||||
add_onion_secret_key_t pk;
|
||||
const char *key_new_alg = NULL;
|
||||
char *key_new_blob = NULL;
|
||||
char *err_msg = NULL;
|
||||
|
||||
pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
|
||||
&key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
if (!pk) {
|
||||
if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
|
||||
&key_new_alg, &key_new_blob, &pk, &hs_version,
|
||||
&err_msg) < 0) {
|
||||
if (err_msg) {
|
||||
connection_write_str_to_buf(err_msg, conn);
|
||||
tor_free(err_msg);
|
||||
@ -4568,16 +4692,23 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
}
|
||||
tor_assert(!err_msg);
|
||||
|
||||
/* Hidden service version 3 don't have client authentication support so if
|
||||
* ClientAuth was given, send back an error. */
|
||||
if (hs_version == HS_VERSION_THREE && auth_clients) {
|
||||
connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Create the HS, using private key pk, client authentication auth_type,
|
||||
* the list of auth_clients, and port config port_cfg.
|
||||
* rend_service_add_ephemeral() will take ownership of pk and port_cfg,
|
||||
* regardless of success/failure.
|
||||
*/
|
||||
char *service_id = NULL;
|
||||
int ret = rend_service_add_ephemeral(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, &service_id);
|
||||
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
|
||||
auth_clients = NULL; /* so is auth_clients */
|
||||
switch (ret) {
|
||||
@ -4670,9 +4801,10 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
* Note: The error messages returned are deliberately vague to avoid echoing
|
||||
* key material.
|
||||
*/
|
||||
STATIC crypto_pk_t *
|
||||
STATIC int
|
||||
add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
const char **key_new_alg_out, char **key_new_blob_out,
|
||||
add_onion_secret_key_t *decoded_key, int *hs_version,
|
||||
char **err_msg_out)
|
||||
{
|
||||
smartlist_t *key_args = smartlist_new();
|
||||
@ -4680,7 +4812,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
const char *key_new_alg = NULL;
|
||||
char *key_new_blob = NULL;
|
||||
char *err_msg = NULL;
|
||||
int ok = 0;
|
||||
int ret = -1;
|
||||
|
||||
smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
|
||||
if (smartlist_len(key_args) != 2) {
|
||||
@ -4692,6 +4824,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
static const char *key_type_new = "NEW";
|
||||
static const char *key_type_best = "BEST";
|
||||
static const char *key_type_rsa1024 = "RSA1024";
|
||||
static const char *key_type_ed25519_v3 = "ED25519-V3";
|
||||
|
||||
const char *key_type = smartlist_get(key_args, 0);
|
||||
const char *key_blob = smartlist_get(key_args, 1);
|
||||
@ -4704,9 +4837,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
goto err;
|
||||
}
|
||||
if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
|
||||
crypto_pk_free(pk);
|
||||
err_msg = tor_strdup("512 Invalid RSA key size\r\n");
|
||||
goto err;
|
||||
}
|
||||
decoded_key->v2 = pk;
|
||||
*hs_version = HS_VERSION_TWO;
|
||||
} else if (!strcasecmp(key_type_ed25519_v3, key_type)) {
|
||||
/* "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,
|
||||
strlen(key_blob)) != sizeof(sk->seckey)) {
|
||||
tor_free(sk);
|
||||
err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n");
|
||||
goto err;
|
||||
}
|
||||
decoded_key->v3 = sk;
|
||||
*hs_version = HS_VERSION_THREE;
|
||||
} else if (!strcasecmp(key_type_new, key_type)) {
|
||||
/* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
|
||||
if (!strcasecmp(key_type_rsa1024, key_blob) ||
|
||||
@ -4720,12 +4867,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
}
|
||||
if (!discard_pk) {
|
||||
if (crypto_pk_base64_encode(pk, &key_new_blob)) {
|
||||
crypto_pk_free(pk);
|
||||
tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
|
||||
key_type_rsa1024);
|
||||
goto err;
|
||||
}
|
||||
key_new_alg = key_type_rsa1024;
|
||||
}
|
||||
decoded_key->v2 = pk;
|
||||
*hs_version = HS_VERSION_TWO;
|
||||
} else if (!strcasecmp(key_type_ed25519_v3, key_blob)) {
|
||||
ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk));
|
||||
if (ed25519_secret_key_generate(sk, 1) < 0) {
|
||||
tor_free(sk);
|
||||
tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
|
||||
key_type_ed25519_v3);
|
||||
goto err;
|
||||
}
|
||||
if (!discard_pk) {
|
||||
ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1;
|
||||
key_new_blob = tor_malloc_zero(len);
|
||||
if (base64_encode(key_new_blob, len, (const char *) sk->seckey,
|
||||
sizeof(sk->seckey), 0) != (len - 1)) {
|
||||
tor_free(sk);
|
||||
tor_free(key_new_blob);
|
||||
tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
|
||||
key_type_ed25519_v3);
|
||||
goto err;
|
||||
}
|
||||
key_new_alg = key_type_ed25519_v3;
|
||||
}
|
||||
decoded_key->v3 = sk;
|
||||
*hs_version = HS_VERSION_THREE;
|
||||
} else {
|
||||
err_msg = tor_strdup("513 Invalid key type\r\n");
|
||||
goto err;
|
||||
@ -4736,8 +4909,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
}
|
||||
|
||||
/* Succeded in loading or generating a private key. */
|
||||
tor_assert(pk);
|
||||
ok = 1;
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
SMARTLIST_FOREACH(key_args, char *, cp, {
|
||||
@ -4746,10 +4918,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
});
|
||||
smartlist_free(key_args);
|
||||
|
||||
if (!ok) {
|
||||
crypto_pk_free(pk);
|
||||
pk = NULL;
|
||||
}
|
||||
if (err_msg_out) {
|
||||
*err_msg_out = err_msg;
|
||||
} else {
|
||||
@ -4758,7 +4926,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
*key_new_alg_out = key_new_alg;
|
||||
*key_new_blob_out = key_new_blob;
|
||||
|
||||
return pk;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Helper function to handle parsing a ClientAuth argument to the
|
||||
@ -4827,6 +4995,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);
|
||||
@ -4834,7 +5003,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;
|
||||
}
|
||||
@ -4861,8 +5034,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.
|
||||
*/
|
||||
@ -4932,9 +5117,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) {
|
||||
@ -7012,27 +7204,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address)
|
||||
* <b>rend_query</b> is used to fetch requested onion address and auth type.
|
||||
* <b>hs_dir</b> is the description of contacting hs directory.
|
||||
* <b>desc_id_base32</b> is the ID of requested hs descriptor.
|
||||
* <b>hsdir_index</b> is the HSDir fetch index value for v3, an hex string.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_requested(const rend_data_t *rend_query,
|
||||
control_event_hs_descriptor_requested(const char *onion_address,
|
||||
rend_auth_type_t auth_type,
|
||||
const char *id_digest,
|
||||
const char *desc_id_base32)
|
||||
const char *desc_id,
|
||||
const char *hsdir_index)
|
||||
{
|
||||
if (!id_digest || !rend_query || !desc_id_base32) {
|
||||
log_warn(LD_BUG, "Called with rend_query==%p, "
|
||||
"id_digest==%p, desc_id_base32==%p",
|
||||
rend_query, id_digest, desc_id_base32);
|
||||
char *hsdir_index_field = NULL;
|
||||
|
||||
if (BUG(!id_digest || !desc_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hsdir_index) {
|
||||
tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
|
||||
}
|
||||
|
||||
send_control_event(EVENT_HS_DESC,
|
||||
"650 HS_DESC REQUESTED %s %s %s %s\r\n",
|
||||
rend_hsaddress_str_or_unknown(
|
||||
rend_data_get_address(rend_query)),
|
||||
rend_auth_type_to_string(
|
||||
TO_REND_DATA_V2(rend_query)->auth_type),
|
||||
"650 HS_DESC REQUESTED %s %s %s %s%s\r\n",
|
||||
rend_hsaddress_str_or_unknown(onion_address),
|
||||
rend_auth_type_to_string(auth_type),
|
||||
node_describe_longname_by_id(id_digest),
|
||||
desc_id_base32);
|
||||
desc_id,
|
||||
hsdir_index_field ? hsdir_index_field : "");
|
||||
tor_free(hsdir_index_field);
|
||||
}
|
||||
|
||||
/** For an HS descriptor query <b>rend_data</b>, using the
|
||||
@ -7081,89 +7279,87 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
|
||||
|
||||
/** send HS_DESC CREATED event when a local service generates a descriptor.
|
||||
*
|
||||
* <b>service_id</b> is the descriptor onion address.
|
||||
* <b>desc_id_base32</b> is the descriptor ID.
|
||||
* <b>replica</b> is the the descriptor replica number.
|
||||
* <b>onion_address</b> is service address.
|
||||
* <b>desc_id</b> is the descriptor ID.
|
||||
* <b>replica</b> is the the descriptor replica number. If it is negative, it
|
||||
* is ignored.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_created(const char *service_id,
|
||||
const char *desc_id_base32,
|
||||
control_event_hs_descriptor_created(const char *onion_address,
|
||||
const char *desc_id,
|
||||
int replica)
|
||||
{
|
||||
if (!service_id || !desc_id_base32) {
|
||||
log_warn(LD_BUG, "Called with service_digest==%p, "
|
||||
"desc_id_base32==%p", service_id, desc_id_base32);
|
||||
char *replica_field = NULL;
|
||||
|
||||
if (BUG(!onion_address || !desc_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (replica >= 0) {
|
||||
tor_asprintf(&replica_field, " REPLICA=%d", replica);
|
||||
}
|
||||
|
||||
send_control_event(EVENT_HS_DESC,
|
||||
"650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
|
||||
"REPLICA=%d\r\n",
|
||||
service_id,
|
||||
desc_id_base32,
|
||||
replica);
|
||||
"650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s%s\r\n",
|
||||
onion_address, desc_id,
|
||||
replica_field ? replica_field : "");
|
||||
tor_free(replica_field);
|
||||
}
|
||||
|
||||
/** send HS_DESC upload event.
|
||||
*
|
||||
* <b>service_id</b> is the descriptor onion address.
|
||||
* <b>onion_address</b> is service address.
|
||||
* <b>hs_dir</b> is the description of contacting hs directory.
|
||||
* <b>desc_id_base32</b> is the ID of requested hs descriptor.
|
||||
* <b>desc_id</b> is the ID of requested hs descriptor.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_upload(const char *service_id,
|
||||
control_event_hs_descriptor_upload(const char *onion_address,
|
||||
const char *id_digest,
|
||||
const char *desc_id_base32)
|
||||
const char *desc_id,
|
||||
const char *hsdir_index)
|
||||
{
|
||||
if (!service_id || !id_digest || !desc_id_base32) {
|
||||
log_warn(LD_BUG, "Called with service_digest==%p, "
|
||||
"desc_id_base32==%p, id_digest==%p", service_id,
|
||||
desc_id_base32, id_digest);
|
||||
char *hsdir_index_field = NULL;
|
||||
|
||||
if (BUG(!onion_address || !id_digest || !desc_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hsdir_index) {
|
||||
tor_asprintf(&hsdir_index_field, " HSDIR_INDEX=%s", hsdir_index);
|
||||
}
|
||||
|
||||
send_control_event(EVENT_HS_DESC,
|
||||
"650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
|
||||
service_id,
|
||||
"650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n",
|
||||
onion_address,
|
||||
node_describe_longname_by_id(id_digest),
|
||||
desc_id_base32);
|
||||
desc_id,
|
||||
hsdir_index_field ? hsdir_index_field : "");
|
||||
tor_free(hsdir_index_field);
|
||||
}
|
||||
|
||||
/** send HS_DESC event after got response from hs directory.
|
||||
*
|
||||
* NOTE: this is an internal function used by following functions:
|
||||
* control_event_hs_descriptor_received
|
||||
* control_event_hs_descriptor_failed
|
||||
* control_event_hsv2_descriptor_received
|
||||
* control_event_hsv2_descriptor_failed
|
||||
* control_event_hsv3_descriptor_failed
|
||||
*
|
||||
* So do not call this function directly.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_receive_end(const char *action,
|
||||
const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *id_digest,
|
||||
const char *reason)
|
||||
static void
|
||||
event_hs_descriptor_receive_end(const char *action,
|
||||
const char *onion_address,
|
||||
const char *desc_id,
|
||||
rend_auth_type_t auth_type,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason)
|
||||
{
|
||||
char *desc_id_field = NULL;
|
||||
char *reason_field = NULL;
|
||||
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
||||
const char *desc_id = NULL;
|
||||
|
||||
if (!action || !rend_data || !onion_address) {
|
||||
log_warn(LD_BUG, "Called with action==%p, rend_data==%p, "
|
||||
"onion_address==%p", action, rend_data, onion_address);
|
||||
if (BUG(!action || !onion_address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
desc_id = get_desc_id_from_query(rend_data, id_digest);
|
||||
if (desc_id != NULL) {
|
||||
/* Set the descriptor ID digest to base32 so we can send it. */
|
||||
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
|
||||
DIGEST_LEN);
|
||||
/* Extra whitespace is needed before the value. */
|
||||
tor_asprintf(&desc_id_field, " %s", desc_id_base32);
|
||||
}
|
||||
|
||||
if (reason) {
|
||||
tor_asprintf(&reason_field, " REASON=%s", reason);
|
||||
}
|
||||
@ -7172,14 +7368,13 @@ control_event_hs_descriptor_receive_end(const char *action,
|
||||
"650 HS_DESC %s %s %s %s%s%s\r\n",
|
||||
action,
|
||||
rend_hsaddress_str_or_unknown(onion_address),
|
||||
rend_auth_type_to_string(
|
||||
TO_REND_DATA_V2(rend_data)->auth_type),
|
||||
id_digest ?
|
||||
node_describe_longname_by_id(id_digest) : "UNKNOWN",
|
||||
desc_id_field ? desc_id_field : "",
|
||||
rend_auth_type_to_string(auth_type),
|
||||
hsdir_id_digest ?
|
||||
node_describe_longname_by_id(hsdir_id_digest) :
|
||||
"UNKNOWN",
|
||||
desc_id ? desc_id : "",
|
||||
reason_field ? reason_field : "");
|
||||
|
||||
tor_free(desc_id_field);
|
||||
tor_free(reason_field);
|
||||
}
|
||||
|
||||
@ -7199,9 +7394,7 @@ control_event_hs_descriptor_upload_end(const char *action,
|
||||
{
|
||||
char *reason_field = NULL;
|
||||
|
||||
if (!action || !id_digest) {
|
||||
log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
|
||||
id_digest);
|
||||
if (BUG(!action || !id_digest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7224,17 +7417,54 @@ control_event_hs_descriptor_upload_end(const char *action,
|
||||
* called when we successfully received a hidden service descriptor.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_received(const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *id_digest)
|
||||
control_event_hsv2_descriptor_received(const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *hsdir_id_digest)
|
||||
{
|
||||
if (!rend_data || !id_digest || !onion_address) {
|
||||
log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, "
|
||||
"onion_address==%p", rend_data, id_digest, onion_address);
|
||||
char *desc_id_field = NULL;
|
||||
const char *desc_id;
|
||||
|
||||
if (BUG(!rend_data || !hsdir_id_digest || !onion_address)) {
|
||||
return;
|
||||
}
|
||||
control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
|
||||
rend_data, id_digest, NULL);
|
||||
|
||||
desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
|
||||
if (desc_id != NULL) {
|
||||
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
||||
/* Set the descriptor ID digest to base32 so we can send it. */
|
||||
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
|
||||
DIGEST_LEN);
|
||||
/* Extra whitespace is needed before the value. */
|
||||
tor_asprintf(&desc_id_field, " %s", desc_id_base32);
|
||||
}
|
||||
|
||||
event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
|
||||
TO_REND_DATA_V2(rend_data)->auth_type,
|
||||
hsdir_id_digest, NULL);
|
||||
tor_free(desc_id_field);
|
||||
}
|
||||
|
||||
/* Send HS_DESC RECEIVED event
|
||||
*
|
||||
* Called when we successfully received a hidden service descriptor. */
|
||||
void
|
||||
control_event_hsv3_descriptor_received(const char *onion_address,
|
||||
const char *desc_id,
|
||||
const char *hsdir_id_digest)
|
||||
{
|
||||
char *desc_id_field = NULL;
|
||||
|
||||
if (BUG(!onion_address || !desc_id || !hsdir_id_digest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Because DescriptorID is an optional positional value, we need to add a
|
||||
* whitespace before in order to not be next to the HsDir value. */
|
||||
tor_asprintf(&desc_id_field, " %s", desc_id);
|
||||
|
||||
event_hs_descriptor_receive_end("RECEIVED", onion_address, desc_id_field,
|
||||
REND_NO_AUTH, hsdir_id_digest, NULL);
|
||||
tor_free(desc_id_field);
|
||||
}
|
||||
|
||||
/** send HS_DESC UPLOADED event
|
||||
@ -7245,9 +7475,7 @@ void
|
||||
control_event_hs_descriptor_uploaded(const char *id_digest,
|
||||
const char *onion_address)
|
||||
{
|
||||
if (!id_digest) {
|
||||
log_warn(LD_BUG, "Called with id_digest==%p",
|
||||
id_digest);
|
||||
if (BUG(!id_digest)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -7261,17 +7489,58 @@ control_event_hs_descriptor_uploaded(const char *id_digest,
|
||||
* add it to REASON= field.
|
||||
*/
|
||||
void
|
||||
control_event_hs_descriptor_failed(const rend_data_t *rend_data,
|
||||
const char *id_digest,
|
||||
const char *reason)
|
||||
control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason)
|
||||
{
|
||||
if (!rend_data) {
|
||||
log_warn(LD_BUG, "Called with rend_data==%p", rend_data);
|
||||
char *desc_id_field = NULL;
|
||||
const char *desc_id;
|
||||
|
||||
if (BUG(!rend_data)) {
|
||||
return;
|
||||
}
|
||||
control_event_hs_descriptor_receive_end("FAILED",
|
||||
rend_data_get_address(rend_data),
|
||||
rend_data, id_digest, reason);
|
||||
|
||||
desc_id = get_desc_id_from_query(rend_data, hsdir_id_digest);
|
||||
if (desc_id != NULL) {
|
||||
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
|
||||
/* Set the descriptor ID digest to base32 so we can send it. */
|
||||
base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
|
||||
DIGEST_LEN);
|
||||
/* Extra whitespace is needed before the value. */
|
||||
tor_asprintf(&desc_id_field, " %s", desc_id_base32);
|
||||
}
|
||||
|
||||
event_hs_descriptor_receive_end("FAILED", rend_data_get_address(rend_data),
|
||||
desc_id_field,
|
||||
TO_REND_DATA_V2(rend_data)->auth_type,
|
||||
hsdir_id_digest, reason);
|
||||
tor_free(desc_id_field);
|
||||
}
|
||||
|
||||
/** Send HS_DESC event to inform controller that the query to
|
||||
* <b>onion_address</b> failed to retrieve hidden service descriptor
|
||||
* <b>desc_id</b> from directory identified by <b>hsdir_id_digest</b>. If
|
||||
* NULL, "UNKNOWN" is used. If <b>reason</b> is not NULL, add it to REASON=
|
||||
* field. */
|
||||
void
|
||||
control_event_hsv3_descriptor_failed(const char *onion_address,
|
||||
const char *desc_id,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason)
|
||||
{
|
||||
char *desc_id_field = NULL;
|
||||
|
||||
if (BUG(!onion_address || !desc_id || !reason)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Because DescriptorID is an optional positional value, we need to add a
|
||||
* whitespace before in order to not be next to the HsDir value. */
|
||||
tor_asprintf(&desc_id_field, " %s", desc_id);
|
||||
|
||||
event_hs_descriptor_receive_end("FAILED", onion_address, desc_id_field,
|
||||
REND_NO_AUTH, hsdir_id_digest, reason);
|
||||
tor_free(desc_id_field);
|
||||
}
|
||||
|
||||
/** Send HS_DESC_CONTENT event after completion of a successful fetch from hs
|
||||
@ -7321,9 +7590,7 @@ control_event_hs_descriptor_upload_failed(const char *id_digest,
|
||||
const char *onion_address,
|
||||
const char *reason)
|
||||
{
|
||||
if (!id_digest) {
|
||||
log_warn(LD_BUG, "Called with id_digest==%p",
|
||||
id_digest);
|
||||
if (BUG(!id_digest)) {
|
||||
return;
|
||||
}
|
||||
control_event_hs_descriptor_upload_end("FAILED", onion_address,
|
||||
|
@ -115,32 +115,39 @@ void control_event_transport_launched(const char *mode,
|
||||
tor_addr_t *addr, uint16_t port);
|
||||
const char *rend_auth_type_to_string(rend_auth_type_t auth_type);
|
||||
MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
|
||||
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
|
||||
const char *desc_id_base32,
|
||||
const char *hs_dir);
|
||||
void control_event_hs_descriptor_created(const char *service_id,
|
||||
const char *desc_id_base32,
|
||||
void control_event_hs_descriptor_requested(const char *onion_address,
|
||||
rend_auth_type_t auth_type,
|
||||
const char *id_digest,
|
||||
const char *desc_id,
|
||||
const char *hsdir_index);
|
||||
void control_event_hs_descriptor_created(const char *onion_address,
|
||||
const char *desc_id,
|
||||
int replica);
|
||||
void control_event_hs_descriptor_upload(const char *service_id,
|
||||
const char *desc_id_base32,
|
||||
const char *hs_dir);
|
||||
void control_event_hs_descriptor_receive_end(const char *action,
|
||||
const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *id_digest,
|
||||
const char *reason);
|
||||
void control_event_hs_descriptor_upload(const char *onion_address,
|
||||
const char *desc_id,
|
||||
const char *hs_dir,
|
||||
const char *hsdir_index);
|
||||
void control_event_hs_descriptor_upload_end(const char *action,
|
||||
const char *onion_address,
|
||||
const char *hs_dir,
|
||||
const char *reason);
|
||||
void control_event_hs_descriptor_received(const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *id_digest);
|
||||
void control_event_hs_descriptor_uploaded(const char *hs_dir,
|
||||
const char *onion_address);
|
||||
void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
|
||||
const char *id_digest,
|
||||
const char *reason);
|
||||
/* Hidden service v2 HS_DESC specific. */
|
||||
void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data,
|
||||
const char *id_digest,
|
||||
const char *reason);
|
||||
void control_event_hsv2_descriptor_received(const char *onion_address,
|
||||
const rend_data_t *rend_data,
|
||||
const char *id_digest);
|
||||
/* Hidden service v3 HS_DESC specific. */
|
||||
void control_event_hsv3_descriptor_failed(const char *onion_address,
|
||||
const char *desc_id,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason);
|
||||
void control_event_hsv3_descriptor_received(const char *onion_address,
|
||||
const char *desc_id,
|
||||
const char *hsdir_id_digest);
|
||||
void control_event_hs_descriptor_upload_failed(const char *hs_dir,
|
||||
const char *onion_address,
|
||||
const char *reason);
|
||||
@ -256,10 +263,22 @@ void format_cell_stats(char **event_string, circuit_t *circ,
|
||||
cell_stats_t *cell_stats);
|
||||
STATIC char *get_bw_samples(void);
|
||||
|
||||
STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
const char **key_new_alg_out,
|
||||
char **key_new_blob_out,
|
||||
char **err_msg_out);
|
||||
/* ADD_ONION secret key to create an ephemeral service. The command supports
|
||||
* multiple versions so this union stores the key and passes it to the HS
|
||||
* subsystem depending on the requested version. */
|
||||
typedef union add_onion_secret_key_t {
|
||||
/* Hidden service v2 secret key. */
|
||||
crypto_pk_t *v2;
|
||||
/* Hidden service v3 secret key. */
|
||||
ed25519_secret_key_t *v3;
|
||||
} add_onion_secret_key_t;
|
||||
|
||||
STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk,
|
||||
const char **key_new_alg_out,
|
||||
char **key_new_blob_out,
|
||||
add_onion_secret_key_t *decoded_key,
|
||||
int *hs_version, char **err_msg_out);
|
||||
|
||||
STATIC rend_authorized_client_t *
|
||||
add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "geoip.h"
|
||||
#include "hs_cache.h"
|
||||
#include "hs_common.h"
|
||||
#include "hs_control.h"
|
||||
#include "hs_client.h"
|
||||
#include "main.h"
|
||||
#include "microdesc.h"
|
||||
@ -3090,10 +3091,19 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
|
||||
/* We got something: Try storing it in the cache. */
|
||||
if (hs_cache_store_as_client(body, &conn->hs_ident->identity_pk) < 0) {
|
||||
log_warn(LD_REND, "Failed to store hidden service descriptor");
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"BAD_DESC");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
} else {
|
||||
log_info(LD_REND, "Stored hidden service descriptor successfully.");
|
||||
TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC;
|
||||
hs_client_desc_has_arrived(conn->hs_ident);
|
||||
/* Fire control port RECEIVED event. */
|
||||
hs_control_desc_event_received(conn->hs_ident, conn->identity_digest);
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
body);
|
||||
}
|
||||
break;
|
||||
case 404:
|
||||
@ -3101,13 +3111,22 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
|
||||
* tries to clean this conn up. */
|
||||
log_info(LD_REND, "Fetching hidden service v3 descriptor not found: "
|
||||
"Retrying at another directory.");
|
||||
/* TODO: Inform the control port */
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"NOT_FOUND");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
case 400:
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
"http status 400 (%s). Dirserver didn't like our "
|
||||
"query? Retrying at another directory.",
|
||||
escaped(reason));
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"QUERY_REJECTED");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
default:
|
||||
log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: "
|
||||
@ -3115,6 +3134,11 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
|
||||
"'%s:%d'. Retrying at another directory.",
|
||||
status_code, escaped(reason), TO_CONN(conn)->address,
|
||||
TO_CONN(conn)->port);
|
||||
/* Fire control port FAILED event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"UNEXPECTED");
|
||||
hs_control_desc_event_content(conn->hs_ident, conn->identity_digest,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3136,9 +3160,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
|
||||
const size_t body_len = args->body_len;
|
||||
|
||||
#define SEND_HS_DESC_FAILED_EVENT(reason) \
|
||||
(control_event_hs_descriptor_failed(conn->rend_data, \
|
||||
conn->identity_digest, \
|
||||
reason))
|
||||
(control_event_hsv2_descriptor_failed(conn->rend_data, \
|
||||
conn->identity_digest, \
|
||||
reason))
|
||||
#define SEND_HS_DESC_FAILED_CONTENT() \
|
||||
(control_event_hs_descriptor_content( \
|
||||
rend_data_get_address(conn->rend_data), \
|
||||
@ -3173,9 +3197,9 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn,
|
||||
/* success. notify pending connections about this. */
|
||||
log_info(LD_REND, "Successfully fetched v2 rendezvous "
|
||||
"descriptor.");
|
||||
control_event_hs_descriptor_received(service_id,
|
||||
conn->rend_data,
|
||||
conn->identity_digest);
|
||||
control_event_hsv2_descriptor_received(service_id,
|
||||
conn->rend_data,
|
||||
conn->identity_digest);
|
||||
control_event_hs_descriptor_content(service_id,
|
||||
conn->requested_resource,
|
||||
conn->identity_digest,
|
||||
@ -3292,7 +3316,7 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
|
||||
case 200:
|
||||
log_info(LD_REND, "Uploading hidden service descriptor: "
|
||||
"finished with status 200 (%s)", escaped(reason));
|
||||
/* XXX: Trigger control event. */
|
||||
hs_control_desc_event_uploaded(conn->hs_ident, conn->identity_digest);
|
||||
break;
|
||||
case 400:
|
||||
log_fn(LOG_PROTOCOL_WARN, LD_REND,
|
||||
@ -3300,7 +3324,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
|
||||
"status 400 (%s) response from dirserver "
|
||||
"'%s:%d'. Malformed hidden service descriptor?",
|
||||
escaped(reason), conn->base_.address, conn->base_.port);
|
||||
/* XXX: Trigger control event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"UPLOAD_REJECTED");
|
||||
break;
|
||||
default:
|
||||
log_warn(LD_REND, "Uploading hidden service descriptor: http "
|
||||
@ -3308,7 +3333,8 @@ handle_response_upload_hsdesc(dir_connection_t *conn,
|
||||
"'%s:%d').",
|
||||
status_code, escaped(reason), conn->base_.address,
|
||||
conn->base_.port);
|
||||
/* XXX: Trigger control event. */
|
||||
hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest,
|
||||
"UNEXPECTED");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -705,6 +705,24 @@ cache_clean_v3_as_client(time_t now)
|
||||
return bytes_removed;
|
||||
}
|
||||
|
||||
/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
|
||||
* its HS encoded descriptor if it's stored in our cache, or NULL if not. */
|
||||
const char *
|
||||
hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key)
|
||||
{
|
||||
hs_cache_client_descriptor_t *cached_desc = NULL;
|
||||
|
||||
tor_assert(key);
|
||||
|
||||
cached_desc = lookup_v3_desc_as_client(key->pubkey);
|
||||
if (cached_desc) {
|
||||
tor_assert(cached_desc->encoded_desc);
|
||||
return cached_desc->encoded_desc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Public API: Given the HS ed25519 identity public key in <b>key</b>, return
|
||||
* its HS descriptor if it's stored in our cache, or NULL if not. */
|
||||
const hs_descriptor_t *
|
||||
|
@ -81,6 +81,8 @@ int hs_cache_lookup_as_dir(uint32_t version, const char *query,
|
||||
|
||||
const hs_descriptor_t *
|
||||
hs_cache_lookup_as_client(const ed25519_public_key_t *key);
|
||||
const char *
|
||||
hs_cache_lookup_encoded_as_client(const ed25519_public_key_t *key);
|
||||
int hs_cache_store_as_client(const char *desc_str,
|
||||
const ed25519_public_key_t *identity_pk);
|
||||
void hs_cache_clean_as_client(time_t now);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
#include "directory.h"
|
||||
#include "hs_client.h"
|
||||
#include "hs_control.h"
|
||||
#include "router.h"
|
||||
#include "routerset.h"
|
||||
#include "circuitlist.h"
|
||||
@ -349,6 +350,10 @@ directory_launch_v3_desc_fetch(const ed25519_public_key_t *onion_identity_pk,
|
||||
safe_str_client(base64_blinded_pubkey),
|
||||
safe_str_client(routerstatus_describe(hsdir)));
|
||||
|
||||
/* Fire a REQUESTED event on the control port. */
|
||||
hs_control_desc_event_requested(onion_identity_pk, base64_blinded_pubkey,
|
||||
hsdir);
|
||||
|
||||
/* Cleanup memory. */
|
||||
memwipe(&blinded_pubkey, 0, sizeof(blinded_pubkey));
|
||||
memwipe(base64_blinded_pubkey, 0, sizeof(base64_blinded_pubkey));
|
||||
|
@ -130,6 +130,17 @@ typedef enum {
|
||||
HS_AUTH_KEY_TYPE_ED25519 = 2,
|
||||
} hs_auth_key_type_t;
|
||||
|
||||
/* Return value when adding an ephemeral service through the ADD_ONION
|
||||
* control port command. Both v2 and v3 share these. */
|
||||
typedef enum {
|
||||
RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
|
||||
RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
|
||||
RSAE_ADDREXISTS = -3, /**< Onion address collision */
|
||||
RSAE_BADPRIVKEY = -2, /**< Invalid public key */
|
||||
RSAE_INTERNAL = -1, /**< Internal error */
|
||||
RSAE_OKAY = 0 /**< Service added as expected */
|
||||
} hs_service_add_ephemeral_status_t;
|
||||
|
||||
/* Represents the mapping from a virtual port of a rendezvous service to a
|
||||
* real port on some IP. */
|
||||
typedef struct rend_service_port_config_t {
|
||||
|
256
src/or/hs_control.c
Normal file
256
src/or/hs_control.c
Normal file
@ -0,0 +1,256 @@
|
||||
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file hs_control.c
|
||||
* \brief Contains control port event related code.
|
||||
**/
|
||||
|
||||
#include "or.h"
|
||||
#include "control.h"
|
||||
#include "hs_common.h"
|
||||
#include "hs_control.h"
|
||||
#include "hs_descriptor.h"
|
||||
#include "hs_service.h"
|
||||
#include "nodelist.h"
|
||||
|
||||
/* Send on the control port the "HS_DESC REQUESTED [...]" event.
|
||||
*
|
||||
* The onion_pk is the onion service public key, base64_blinded_pk is the
|
||||
* base64 encoded blinded key for the service and hsdir_rs is the routerstatus
|
||||
* object of the HSDir that this request is for. */
|
||||
void
|
||||
hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
|
||||
const char *base64_blinded_pk,
|
||||
const routerstatus_t *hsdir_rs)
|
||||
{
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
const uint8_t *hsdir_index;
|
||||
const node_t *hsdir_node;
|
||||
|
||||
tor_assert(onion_pk);
|
||||
tor_assert(base64_blinded_pk);
|
||||
tor_assert(hsdir_rs);
|
||||
|
||||
hs_build_address(onion_pk, HS_VERSION_THREE, onion_address);
|
||||
|
||||
/* Get the node from the routerstatus object to get the HSDir index used for
|
||||
* this request. We can't have a routerstatus entry without a node and we
|
||||
* can't pick a node without an hsdir_index. */
|
||||
hsdir_node = node_get_by_id(hsdir_rs->identity_digest);
|
||||
tor_assert(hsdir_node);
|
||||
tor_assert(hsdir_node->hsdir_index);
|
||||
/* This is a fetch event. */
|
||||
hsdir_index = hsdir_node->hsdir_index->fetch;
|
||||
|
||||
/* Trigger the event. */
|
||||
control_event_hs_descriptor_requested(onion_address, REND_NO_AUTH,
|
||||
hsdir_rs->identity_digest,
|
||||
base64_blinded_pk,
|
||||
hex_str((const char *) hsdir_index,
|
||||
DIGEST256_LEN));
|
||||
memwipe(onion_address, 0, sizeof(onion_address));
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC FAILED [...]" event.
|
||||
*
|
||||
* Using a directory connection identifier, the HSDir identity digest and a
|
||||
* reason for the failure. None can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason)
|
||||
{
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
|
||||
tor_assert(ident);
|
||||
tor_assert(hsdir_id_digest);
|
||||
tor_assert(reason);
|
||||
|
||||
/* Build onion address and encoded blinded key. */
|
||||
IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
|
||||
&ident->blinded_pk) < 0) {
|
||||
return;
|
||||
}
|
||||
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
|
||||
|
||||
control_event_hsv3_descriptor_failed(onion_address, base64_blinded_pk,
|
||||
hsdir_id_digest, reason);
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC RECEIVED [...]" event.
|
||||
*
|
||||
* Using a directory connection identifier and the HSDir identity digest.
|
||||
* None can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest)
|
||||
{
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
|
||||
tor_assert(ident);
|
||||
tor_assert(hsdir_id_digest);
|
||||
|
||||
/* Build onion address and encoded blinded key. */
|
||||
IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
|
||||
&ident->blinded_pk) < 0) {
|
||||
return;
|
||||
}
|
||||
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
|
||||
|
||||
control_event_hsv3_descriptor_received(onion_address, base64_blinded_pk,
|
||||
hsdir_id_digest);
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC CREATED [...]" event.
|
||||
*
|
||||
* Using the onion address of the descriptor's service and the blinded public
|
||||
* key of the descriptor as a descriptor ID. None can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_created(const char *onion_address,
|
||||
const ed25519_public_key_t *blinded_pk)
|
||||
{
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
|
||||
tor_assert(onion_address);
|
||||
tor_assert(blinded_pk);
|
||||
|
||||
/* Build base64 encoded blinded key. */
|
||||
IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Version 3 doesn't use the replica number in its descriptor ID computation
|
||||
* so we pass negative value so the control port subsystem can ignore it. */
|
||||
control_event_hs_descriptor_created(onion_address, base64_blinded_pk, -1);
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC UPLOAD [...]" event.
|
||||
*
|
||||
* Using the onion address of the descriptor's service, the HSDir identity
|
||||
* digest, the blinded public key of the descriptor as a descriptor ID and the
|
||||
* HSDir index for this particular request. None can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_upload(const char *onion_address,
|
||||
const char *hsdir_id_digest,
|
||||
const ed25519_public_key_t *blinded_pk,
|
||||
const uint8_t *hsdir_index)
|
||||
{
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
|
||||
tor_assert(onion_address);
|
||||
tor_assert(hsdir_id_digest);
|
||||
tor_assert(blinded_pk);
|
||||
tor_assert(hsdir_index);
|
||||
|
||||
/* Build base64 encoded blinded key. */
|
||||
IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk, blinded_pk) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
control_event_hs_descriptor_upload(onion_address, hsdir_id_digest,
|
||||
base64_blinded_pk,
|
||||
hex_str((const char *) hsdir_index,
|
||||
DIGEST256_LEN));
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC UPLOADED [...]" event.
|
||||
*
|
||||
* Using the directory connection identifier and the HSDir identity digest.
|
||||
* None can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest)
|
||||
{
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
|
||||
tor_assert(ident);
|
||||
tor_assert(hsdir_id_digest);
|
||||
|
||||
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
|
||||
|
||||
control_event_hs_descriptor_uploaded(hsdir_id_digest, onion_address);
|
||||
}
|
||||
|
||||
/* Send on the control port the "HS_DESC_CONTENT [...]" event.
|
||||
*
|
||||
* Using the directory connection identifier, the HSDir identity digest and
|
||||
* the body of the descriptor (as it was received from the directory). None
|
||||
* can be NULL. */
|
||||
void
|
||||
hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest,
|
||||
const char *body)
|
||||
{
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
|
||||
tor_assert(ident);
|
||||
tor_assert(hsdir_id_digest);
|
||||
|
||||
/* Build onion address and encoded blinded key. */
|
||||
IF_BUG_ONCE(ed25519_public_to_base64(base64_blinded_pk,
|
||||
&ident->blinded_pk) < 0) {
|
||||
return;
|
||||
}
|
||||
hs_build_address(&ident->identity_pk, HS_VERSION_THREE, onion_address);
|
||||
|
||||
control_event_hs_descriptor_content(onion_address, base64_blinded_pk,
|
||||
hsdir_id_digest, body);
|
||||
}
|
||||
|
||||
/* Handle the "HSPOST [...]" command. The body is an encoded descriptor for
|
||||
* the given onion_address. The descriptor will be uploaded to each directory
|
||||
* in hsdirs_rs. If NULL, the responsible directories for the current time
|
||||
* period will be selected.
|
||||
*
|
||||
* Return -1 on if the descriptor plaintext section is not decodable. Else, 0
|
||||
* on success. */
|
||||
int
|
||||
hs_control_hspost_command(const char *body, const char *onion_address,
|
||||
const smartlist_t *hsdirs_rs)
|
||||
{
|
||||
int ret = -1;
|
||||
ed25519_public_key_t identity_pk;
|
||||
hs_desc_plaintext_data_t plaintext;
|
||||
smartlist_t *hsdirs = NULL;
|
||||
|
||||
tor_assert(body);
|
||||
tor_assert(onion_address);
|
||||
|
||||
/* This can't fail because we require the caller to pass us a valid onion
|
||||
* address that has passed hs_address_is_valid(). */
|
||||
hs_parse_address(onion_address, &identity_pk, NULL, NULL);
|
||||
|
||||
/* Only decode the plaintext part which is what the directory will do to
|
||||
* validate before caching. */
|
||||
if (hs_desc_decode_plaintext(body, &plaintext) < 0) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* No HSDir(s) given, we'll compute what the current ones should be. */
|
||||
if (hsdirs_rs == NULL) {
|
||||
hsdirs = smartlist_new();
|
||||
hs_get_responsible_hsdirs(&plaintext.blinded_pubkey,
|
||||
hs_get_time_period_num(0),
|
||||
0, /* Always the current descriptor which uses
|
||||
* the first hsdir index. */
|
||||
0, /* It is for storing on a directory. */
|
||||
hsdirs);
|
||||
hsdirs_rs = hsdirs;
|
||||
}
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(hsdirs_rs, const routerstatus_t *, rs) {
|
||||
hs_service_upload_desc_to_dir(body, plaintext.version, &identity_pk,
|
||||
&plaintext.blinded_pubkey, rs);
|
||||
} SMARTLIST_FOREACH_END(rs);
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
/* We don't have ownership of the objects in this list. */
|
||||
smartlist_free(hsdirs);
|
||||
return ret;
|
||||
}
|
||||
|
52
src/or/hs_control.h
Normal file
52
src/or/hs_control.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file hs_control.h
|
||||
* \brief Header file containing control port event related code.
|
||||
**/
|
||||
|
||||
#ifndef TOR_HS_CONTROL_H
|
||||
#define TOR_HS_CONTROL_H
|
||||
|
||||
#include "hs_ident.h"
|
||||
|
||||
/* Event "HS_DESC REQUESTED [...]" */
|
||||
void hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk,
|
||||
const char *base64_blinded_pk,
|
||||
const routerstatus_t *hsdir_rs);
|
||||
|
||||
/* Event "HS_DESC FAILED [...]" */
|
||||
void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest,
|
||||
const char *reason);
|
||||
|
||||
/* Event "HS_DESC RECEIVED [...]" */
|
||||
void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest);
|
||||
|
||||
/* Event "HS_DESC CREATED [...]" */
|
||||
void hs_control_desc_event_created(const char *onion_address,
|
||||
const ed25519_public_key_t *blinded_pk);
|
||||
|
||||
/* Event "HS_DESC UPLOAD [...]" */
|
||||
void hs_control_desc_event_upload(const char *onion_address,
|
||||
const char *hsdir_id_digest,
|
||||
const ed25519_public_key_t *blinded_pk,
|
||||
const uint8_t *hsdir_index);
|
||||
|
||||
/* Event "HS_DESC UPLOADED [...]" */
|
||||
void hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest);
|
||||
|
||||
/* Event "HS_DESC_CONTENT [...]" */
|
||||
void hs_control_desc_event_content(const hs_ident_dir_conn_t *ident,
|
||||
const char *hsdir_id_digest,
|
||||
const char *body);
|
||||
|
||||
/* Command "HSPOST [...]" */
|
||||
int hs_control_hspost_command(const char *body, const char *onion_address,
|
||||
const smartlist_t *hsdirs_rs);
|
||||
|
||||
#endif /* !defined(TOR_HS_CONTROL_H) */
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "hs_circuit.h"
|
||||
#include "hs_common.h"
|
||||
#include "hs_config.h"
|
||||
#include "hs_control.h"
|
||||
#include "hs_circuit.h"
|
||||
#include "hs_descriptor.h"
|
||||
#include "hs_ident.h"
|
||||
@ -1431,6 +1432,9 @@ build_service_descriptor(hs_service_t *service, time_t now,
|
||||
|
||||
/* Assign newly built descriptor to the next slot. */
|
||||
*desc_out = desc;
|
||||
/* Fire a CREATED control port event. */
|
||||
hs_control_desc_event_created(service->onion_address,
|
||||
&desc->blinded_kp.pubkey);
|
||||
return;
|
||||
|
||||
err:
|
||||
@ -2199,16 +2203,12 @@ static void
|
||||
upload_descriptor_to_hsdir(const hs_service_t *service,
|
||||
hs_service_descriptor_t *desc, const node_t *hsdir)
|
||||
{
|
||||
char version_str[4] = {0}, *encoded_desc = NULL;
|
||||
directory_request_t *dir_req;
|
||||
hs_ident_dir_conn_t ident;
|
||||
char *encoded_desc = NULL;
|
||||
|
||||
tor_assert(service);
|
||||
tor_assert(desc);
|
||||
tor_assert(hsdir);
|
||||
|
||||
memset(&ident, 0, sizeof(ident));
|
||||
|
||||
/* Let's avoid doing that if tor is configured to not publish. */
|
||||
if (!get_options()->PublishHidServDescriptors) {
|
||||
log_info(LD_REND, "Service %s not publishing descriptor. "
|
||||
@ -2224,29 +2224,10 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Setup the connection identifier. */
|
||||
hs_ident_dir_conn_init(&service->keys.identity_pk, &desc->blinded_kp.pubkey,
|
||||
&ident);
|
||||
|
||||
/* This is our resource when uploading which is used to construct the URL
|
||||
* with the version number: "/tor/hs/<version>/publish". */
|
||||
tor_snprintf(version_str, sizeof(version_str), "%u",
|
||||
service->config.version);
|
||||
|
||||
/* Build the directory request for this HSDir. */
|
||||
dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
|
||||
directory_request_set_routerstatus(dir_req, hsdir->rs);
|
||||
directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
|
||||
directory_request_set_resource(dir_req, version_str);
|
||||
directory_request_set_payload(dir_req, encoded_desc,
|
||||
strlen(encoded_desc));
|
||||
/* The ident object is copied over the directory connection object once
|
||||
* the directory request is initiated. */
|
||||
directory_request_upload_set_hs_ident(dir_req, &ident);
|
||||
|
||||
/* Initiate the directory request to the hsdir.*/
|
||||
directory_initiate_request(dir_req);
|
||||
directory_request_free(dir_req);
|
||||
/* Time to upload the descriptor to the directory. */
|
||||
hs_service_upload_desc_to_dir(encoded_desc, service->config.version,
|
||||
&service->keys.identity_pk,
|
||||
&desc->blinded_kp.pubkey, hsdir->rs);
|
||||
|
||||
/* Add this node to previous_hsdirs list */
|
||||
service_desc_note_upload(desc, hsdir);
|
||||
@ -2263,9 +2244,12 @@ upload_descriptor_to_hsdir(const hs_service_t *service,
|
||||
desc->desc->plaintext_data.revision_counter,
|
||||
safe_str_client(node_describe(hsdir)),
|
||||
safe_str_client(hex_str((const char *) index, 32)));
|
||||
|
||||
/* Fire a UPLOAD control port event. */
|
||||
hs_control_desc_event_upload(service->onion_address, hsdir->identity,
|
||||
&desc->blinded_kp.pubkey, index);
|
||||
}
|
||||
|
||||
/* XXX: Inform control port of the upload event (#20699). */
|
||||
end:
|
||||
tor_free(encoded_desc);
|
||||
return;
|
||||
@ -2900,6 +2884,205 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list)
|
||||
/* Public API */
|
||||
/* ========== */
|
||||
|
||||
/* Upload an encoded descriptor in encoded_desc of the given version. This
|
||||
* descriptor is for the service identity_pk and blinded_pk used to setup the
|
||||
* directory connection identifier. It is uploaded to the directory hsdir_rs
|
||||
* routerstatus_t object.
|
||||
*
|
||||
* NOTE: This function does NOT check for PublishHidServDescriptors because it
|
||||
* is only used by the control port command HSPOST outside of this subsystem.
|
||||
* Inside this code, upload_descriptor_to_hsdir() should be used. */
|
||||
void
|
||||
hs_service_upload_desc_to_dir(const char *encoded_desc,
|
||||
const uint8_t version,
|
||||
const ed25519_public_key_t *identity_pk,
|
||||
const ed25519_public_key_t *blinded_pk,
|
||||
const routerstatus_t *hsdir_rs)
|
||||
{
|
||||
char version_str[4] = {0};
|
||||
directory_request_t *dir_req;
|
||||
hs_ident_dir_conn_t ident;
|
||||
|
||||
tor_assert(encoded_desc);
|
||||
tor_assert(identity_pk);
|
||||
tor_assert(blinded_pk);
|
||||
tor_assert(hsdir_rs);
|
||||
|
||||
/* Setup the connection identifier. */
|
||||
memset(&ident, 0, sizeof(ident));
|
||||
hs_ident_dir_conn_init(identity_pk, blinded_pk, &ident);
|
||||
|
||||
/* This is our resource when uploading which is used to construct the URL
|
||||
* with the version number: "/tor/hs/<version>/publish". */
|
||||
tor_snprintf(version_str, sizeof(version_str), "%u", version);
|
||||
|
||||
/* Build the directory request for this HSDir. */
|
||||
dir_req = directory_request_new(DIR_PURPOSE_UPLOAD_HSDESC);
|
||||
directory_request_set_routerstatus(dir_req, hsdir_rs);
|
||||
directory_request_set_indirection(dir_req, DIRIND_ANONYMOUS);
|
||||
directory_request_set_resource(dir_req, version_str);
|
||||
directory_request_set_payload(dir_req, encoded_desc,
|
||||
strlen(encoded_desc));
|
||||
/* The ident object is copied over the directory connection object once
|
||||
* the directory request is initiated. */
|
||||
directory_request_upload_set_hs_ident(dir_req, &ident);
|
||||
|
||||
/* Initiate the directory request to the hsdir.*/
|
||||
directory_initiate_request(dir_req);
|
||||
directory_request_free(dir_req);
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
char *
|
||||
hs_service_lookup_current_desc(const ed25519_public_key_t *pk)
|
||||
{
|
||||
const hs_service_t *service;
|
||||
|
||||
tor_assert(pk);
|
||||
|
||||
service = find_service(hs_service_map, pk);
|
||||
if (service && service->desc_current) {
|
||||
char *encoded_desc = NULL;
|
||||
/* No matter what is the result (which should never be a failure), return
|
||||
* the encoded variable, if success it will contain the right thing else
|
||||
* it will be NULL. */
|
||||
hs_desc_encode_descriptor(service->desc_current->desc,
|
||||
&service->desc_current->signing_kp,
|
||||
&encoded_desc);
|
||||
return encoded_desc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the number of service we have configured and usable. */
|
||||
unsigned int
|
||||
hs_service_get_num_services(void)
|
||||
@ -2928,7 +3111,9 @@ hs_service_intro_circ_has_closed(origin_circuit_t *circ)
|
||||
|
||||
get_objects_from_ident(circ->hs_ident, &service, &ip, &desc);
|
||||
if (service == NULL) {
|
||||
log_warn(LD_REND, "Unable to find any hidden service associated "
|
||||
/* This is possible if the circuits are closed and the service is
|
||||
* immediately deleted. */
|
||||
log_info(LD_REND, "Unable to find any hidden service associated "
|
||||
"identity key %s on intro circuit %u.",
|
||||
ed25519_fmt(&circ->hs_ident->identity_pk),
|
||||
TO_CIRCUIT(circ)->n_circ_id);
|
||||
|
@ -271,6 +271,21 @@ int hs_service_receive_introduce2(origin_circuit_t *circ,
|
||||
|
||||
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);
|
||||
|
||||
/* Used outside of the HS subsystem by the control port command HSPOST. */
|
||||
void hs_service_upload_desc_to_dir(const char *encoded_desc,
|
||||
const uint8_t version,
|
||||
const ed25519_public_key_t *identity_pk,
|
||||
const ed25519_public_key_t *blinded_pk,
|
||||
const routerstatus_t *hsdir_rs);
|
||||
|
||||
#ifdef HS_SERVICE_PRIVATE
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
@ -60,6 +60,7 @@ LIBTOR_A_SOURCES = \
|
||||
src/or/hs_client.c \
|
||||
src/or/hs_common.c \
|
||||
src/or/hs_config.c \
|
||||
src/or/hs_control.c \
|
||||
src/or/hs_descriptor.c \
|
||||
src/or/hs_ident.c \
|
||||
src/or/hs_intropoint.c \
|
||||
@ -196,11 +197,12 @@ ORHEADERS = \
|
||||
src/or/hibernate.h \
|
||||
src/or/hs_cache.h \
|
||||
src/or/hs_cell.h \
|
||||
src/or/hs_config.h \
|
||||
src/or/hs_circuit.h \
|
||||
src/or/hs_circuitmap.h \
|
||||
src/or/hs_client.h \
|
||||
src/or/hs_common.h \
|
||||
src/or/hs_config.h \
|
||||
src/or/hs_control.h \
|
||||
src/or/hs_descriptor.h \
|
||||
src/or/hs_ident.h \
|
||||
src/or/hs_intropoint.h \
|
||||
|
@ -459,7 +459,8 @@ directory_get_from_hs_dir(const char *desc_id,
|
||||
hs_dir = hs_pick_hsdir(responsible_dirs, desc_id_base32);
|
||||
if (!hs_dir) {
|
||||
/* No suitable hs dir can be found, stop right now. */
|
||||
control_event_hs_descriptor_failed(rend_query, NULL, "QUERY_NO_HSDIR");
|
||||
control_event_hsv2_descriptor_failed(rend_query, NULL,
|
||||
"QUERY_NO_HSDIR");
|
||||
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
|
||||
desc_id_base32, NULL, NULL);
|
||||
return 0;
|
||||
@ -482,7 +483,7 @@ directory_get_from_hs_dir(const char *desc_id,
|
||||
REND_DESC_COOKIE_LEN,
|
||||
0)<0) {
|
||||
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
|
||||
control_event_hs_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
|
||||
control_event_hsv2_descriptor_failed(rend_query, hsdir_fp, "BAD_DESC");
|
||||
control_event_hs_descriptor_content(rend_data_get_address(rend_query),
|
||||
desc_id_base32, hsdir_fp, NULL);
|
||||
return 0;
|
||||
@ -515,9 +516,10 @@ directory_get_from_hs_dir(const char *desc_id,
|
||||
(rend_data->auth_type == REND_NO_AUTH ? "[none]" :
|
||||
escaped_safe_str_client(descriptor_cookie_base64)),
|
||||
routerstatus_describe(hs_dir));
|
||||
control_event_hs_descriptor_requested(rend_query,
|
||||
control_event_hs_descriptor_requested(rend_data->onion_address,
|
||||
rend_data->auth_type,
|
||||
hs_dir->identity_digest,
|
||||
desc_id_base32);
|
||||
desc_id_base32, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -847,9 +847,9 @@ rend_config_service(const config_line_t *line_,
|
||||
* after calling this routine, and may assume that correct cleanup has
|
||||
* been done on failure.
|
||||
*
|
||||
* Return an appropriate rend_service_add_ephemeral_status_t.
|
||||
* Return an appropriate hs_service_add_ephemeral_status_t.
|
||||
*/
|
||||
rend_service_add_ephemeral_status_t
|
||||
hs_service_add_ephemeral_status_t
|
||||
rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
smartlist_t *ports,
|
||||
int max_streams_per_circuit,
|
||||
@ -3576,7 +3576,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
|
||||
"directories to post descriptors to.");
|
||||
control_event_hs_descriptor_upload(service_id,
|
||||
"UNKNOWN",
|
||||
"UNKNOWN");
|
||||
"UNKNOWN", NULL);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
@ -3631,7 +3631,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
|
||||
hs_dir->or_port);
|
||||
control_event_hs_descriptor_upload(service_id,
|
||||
hs_dir->identity_digest,
|
||||
desc_id_base32);
|
||||
desc_id_base32, NULL);
|
||||
tor_free(hs_dir_ip);
|
||||
/* Remember successful upload to this router for next time. */
|
||||
if (!smartlist_contains_digest(successful_uploads,
|
||||
|
@ -187,16 +187,7 @@ void rend_service_port_config_free(rend_service_port_config_t *p);
|
||||
|
||||
void rend_authorized_client_free(rend_authorized_client_t *client);
|
||||
|
||||
/** Return value from rend_service_add_ephemeral. */
|
||||
typedef enum {
|
||||
RSAE_BADAUTH = -5, /**< Invalid auth_type/auth_clients */
|
||||
RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
|
||||
RSAE_ADDREXISTS = -3, /**< Onion address collision */
|
||||
RSAE_BADPRIVKEY = -2, /**< Invalid public key */
|
||||
RSAE_INTERNAL = -1, /**< Internal error */
|
||||
RSAE_OKAY = 0 /**< Service added as expected */
|
||||
} rend_service_add_ephemeral_status_t;
|
||||
rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
hs_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
smartlist_t *ports,
|
||||
int max_streams_per_circuit,
|
||||
int max_streams_close_circuit,
|
||||
|
@ -125,6 +125,7 @@ src_test_test_SOURCES = \
|
||||
src/test/test_hs_service.c \
|
||||
src/test/test_hs_client.c \
|
||||
src/test/test_hs_intropoint.c \
|
||||
src/test/test_hs_control.c \
|
||||
src/test/test_handles.c \
|
||||
src/test/test_hs_cache.c \
|
||||
src/test/test_hs_descriptor.c \
|
||||
|
@ -1201,6 +1201,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "hs_cell/", hs_cell_tests },
|
||||
{ "hs_common/", hs_common_tests },
|
||||
{ "hs_config/", hs_config_tests },
|
||||
{ "hs_control/", hs_control_tests },
|
||||
{ "hs_descriptor/", hs_descriptor },
|
||||
{ "hs_ntor/", hs_ntor_tests },
|
||||
{ "hs_service/", hs_service_tests },
|
||||
|
@ -210,6 +210,7 @@ extern struct testcase_t hs_cache[];
|
||||
extern struct testcase_t hs_cell_tests[];
|
||||
extern struct testcase_t hs_common_tests[];
|
||||
extern struct testcase_t hs_config_tests[];
|
||||
extern struct testcase_t hs_control_tests[];
|
||||
extern struct testcase_t hs_descriptor[];
|
||||
extern struct testcase_t hs_ntor_tests[];
|
||||
extern struct testcase_t hs_service_tests[];
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "bridges.h"
|
||||
#include "control.h"
|
||||
#include "entrynodes.h"
|
||||
#include "hs_common.h"
|
||||
#include "networkstatus.h"
|
||||
#include "rendservice.h"
|
||||
#include "routerlist.h"
|
||||
@ -13,10 +14,87 @@
|
||||
#include "test_helpers.h"
|
||||
|
||||
static void
|
||||
test_add_onion_helper_keyarg(void *arg)
|
||||
test_add_onion_helper_keyarg_v3(void *arg)
|
||||
{
|
||||
crypto_pk_t *pk = NULL;
|
||||
crypto_pk_t *pk2 = NULL;
|
||||
int ret, hs_version;
|
||||
add_onion_secret_key_t pk;
|
||||
char *key_new_blob = NULL;
|
||||
char *err_msg = NULL;
|
||||
const char *key_new_alg = NULL;
|
||||
|
||||
(void) arg;
|
||||
|
||||
memset(&pk, 0, sizeof(pk));
|
||||
|
||||
/* Test explicit ED25519-V3 key generation. */
|
||||
ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg,
|
||||
&key_new_blob, &pk, &hs_version,
|
||||
&err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
|
||||
tt_assert(pk.v3);
|
||||
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.v3); pk.v3 = 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, &hs_version,
|
||||
&err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE);
|
||||
tt_assert(pk.v3);
|
||||
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.v3); pk.v3 = 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, &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.v3);
|
||||
tt_mem_op(pk.v3, 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.v3); pk.v3 = NULL;
|
||||
tor_free(key_new_blob);
|
||||
}
|
||||
|
||||
done:
|
||||
tor_free(pk.v3);
|
||||
tor_free(key_new_blob);
|
||||
tor_free(err_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
test_add_onion_helper_keyarg_v2(void *arg)
|
||||
{
|
||||
int ret, hs_version;
|
||||
add_onion_secret_key_t pk;
|
||||
crypto_pk_t *pk1 = NULL;
|
||||
const char *key_new_alg = NULL;
|
||||
char *key_new_blob = NULL;
|
||||
char *err_msg = NULL;
|
||||
@ -25,83 +103,100 @@ test_add_onion_helper_keyarg(void *arg)
|
||||
|
||||
(void) arg;
|
||||
|
||||
memset(&pk, 0, sizeof(pk));
|
||||
|
||||
/* Test explicit RSA1024 key generation. */
|
||||
pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_assert(pk);
|
||||
ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(pk.v2);
|
||||
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
|
||||
tt_assert(key_new_blob);
|
||||
tt_ptr_op(err_msg, OP_EQ, NULL);
|
||||
|
||||
/* Test "BEST" key generation (Assumes BEST = RSA1024). */
|
||||
crypto_pk_free(pk);
|
||||
crypto_pk_free(pk.v2); pk.v2 = NULL;
|
||||
tor_free(key_new_blob);
|
||||
pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_assert(pk);
|
||||
ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(pk.v2);
|
||||
tt_str_op(key_new_alg, OP_EQ, "RSA1024");
|
||||
tt_assert(key_new_blob);
|
||||
tt_ptr_op(err_msg, OP_EQ, NULL);
|
||||
|
||||
/* Test discarding the private key. */
|
||||
crypto_pk_free(pk);
|
||||
crypto_pk_free(pk.v2); pk.v2 = NULL;
|
||||
tor_free(key_new_blob);
|
||||
pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_assert(pk);
|
||||
ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(pk.v2);
|
||||
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);
|
||||
|
||||
/* Test generating a invalid key type. */
|
||||
crypto_pk_free(pk);
|
||||
pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_ptr_op(pk, OP_EQ, NULL);
|
||||
crypto_pk_free(pk.v2); pk.v2 = NULL;
|
||||
ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(!pk.v2);
|
||||
tt_ptr_op(key_new_alg, OP_EQ, NULL);
|
||||
tt_ptr_op(key_new_blob, OP_EQ, NULL);
|
||||
tt_assert(err_msg);
|
||||
|
||||
/* Test loading a RSA1024 key. */
|
||||
tor_free(err_msg);
|
||||
pk = pk_generate(0);
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded));
|
||||
pk1 = pk_generate(0);
|
||||
tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
|
||||
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
|
||||
pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_assert(pk2);
|
||||
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(pk.v2);
|
||||
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);
|
||||
tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0);
|
||||
tt_int_op(crypto_pk_cmp_keys(pk1, pk.v2), OP_EQ, 0);
|
||||
|
||||
/* Test loading a invalid key type. */
|
||||
tor_free(arg_str);
|
||||
crypto_pk_free(pk); pk = NULL;
|
||||
crypto_pk_free(pk1); pk1 = NULL;
|
||||
crypto_pk_free(pk.v2); pk.v2 = NULL;
|
||||
tor_asprintf(&arg_str, "RSA512:%s", encoded);
|
||||
pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_ptr_op(pk, OP_EQ, NULL);
|
||||
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(!pk.v2);
|
||||
tt_ptr_op(key_new_alg, OP_EQ, NULL);
|
||||
tt_ptr_op(key_new_blob, OP_EQ, NULL);
|
||||
tt_assert(err_msg);
|
||||
|
||||
/* Test loading a invalid key. */
|
||||
tor_free(arg_str);
|
||||
crypto_pk_free(pk); pk = NULL;
|
||||
crypto_pk_free(pk.v2); pk.v2 = NULL;
|
||||
tor_free(err_msg);
|
||||
encoded[strlen(encoded)/2] = '\0';
|
||||
tor_asprintf(&arg_str, "RSA1024:%s", encoded);
|
||||
pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&err_msg);
|
||||
tt_ptr_op(pk, OP_EQ, NULL);
|
||||
ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
|
||||
&pk, &hs_version, &err_msg);
|
||||
tt_int_op(ret, OP_EQ, -1);
|
||||
tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO);
|
||||
tt_assert(!pk.v2);
|
||||
tt_ptr_op(key_new_alg, OP_EQ, NULL);
|
||||
tt_ptr_op(key_new_blob, OP_EQ, NULL);
|
||||
tt_assert(err_msg);
|
||||
|
||||
done:
|
||||
crypto_pk_free(pk);
|
||||
crypto_pk_free(pk2);
|
||||
crypto_pk_free(pk1);
|
||||
crypto_pk_free(pk.v2);
|
||||
tor_free(key_new_blob);
|
||||
tor_free(err_msg);
|
||||
tor_free(encoded);
|
||||
@ -1370,7 +1465,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 },
|
||||
|
@ -258,8 +258,9 @@ test_hs_desc_event(void *arg)
|
||||
sizeof(desc_id_base32));
|
||||
|
||||
/* test request event */
|
||||
control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID,
|
||||
STR_DESC_ID_BASE32);
|
||||
control_event_hs_descriptor_requested(rend_query.onion_address,
|
||||
rend_query.auth_type, HSDIR_EXIST_ID,
|
||||
STR_DESC_ID_BASE32, NULL);
|
||||
expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
|
||||
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
|
||||
tt_assert(received_msg);
|
||||
@ -268,8 +269,8 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test received event */
|
||||
rend_query.auth_type = REND_BASIC_AUTH;
|
||||
control_event_hs_descriptor_received(rend_query.onion_address,
|
||||
&rend_query.base_, HSDIR_EXIST_ID);
|
||||
control_event_hsv2_descriptor_received(rend_query.onion_address,
|
||||
&rend_query.base_, HSDIR_EXIST_ID);
|
||||
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
|
||||
STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
|
||||
tt_assert(received_msg);
|
||||
@ -278,7 +279,7 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test failed event */
|
||||
rend_query.auth_type = REND_STEALTH_AUTH;
|
||||
control_event_hs_descriptor_failed(&rend_query.base_,
|
||||
control_event_hsv2_descriptor_failed(&rend_query.base_,
|
||||
HSDIR_NONE_EXIST_ID,
|
||||
"QUERY_REJECTED");
|
||||
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
|
||||
@ -289,7 +290,7 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test invalid auth type */
|
||||
rend_query.auth_type = 999;
|
||||
control_event_hs_descriptor_failed(&rend_query.base_,
|
||||
control_event_hsv2_descriptor_failed(&rend_query.base_,
|
||||
HSDIR_EXIST_ID,
|
||||
"QUERY_REJECTED");
|
||||
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
|
||||
@ -301,7 +302,7 @@ test_hs_desc_event(void *arg)
|
||||
|
||||
/* test no HSDir fingerprint type */
|
||||
rend_query.auth_type = REND_NO_AUTH;
|
||||
control_event_hs_descriptor_failed(&rend_query.base_, NULL,
|
||||
control_event_hsv2_descriptor_failed(&rend_query.base_, NULL,
|
||||
"QUERY_NO_HSDIR");
|
||||
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" NO_AUTH " \
|
||||
"UNKNOWN REASON=QUERY_NO_HSDIR\r\n";
|
||||
|
199
src/test/test_hs_control.c
Normal file
199
src/test/test_hs_control.c
Normal file
@ -0,0 +1,199 @@
|
||||
/* Copyright (c) 2017, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
/**
|
||||
* \file test_hs_control.c
|
||||
* \brief Unit tests for hidden service control port event and command.
|
||||
**/
|
||||
|
||||
#define CONTROL_PRIVATE
|
||||
#define CIRCUITBUILD_PRIVATE
|
||||
#define RENDCOMMON_PRIVATE
|
||||
#define RENDSERVICE_PRIVATE
|
||||
#define HS_SERVICE_PRIVATE
|
||||
|
||||
#include "or.h"
|
||||
#include "test.h"
|
||||
#include "control.h"
|
||||
#include "config.h"
|
||||
#include "hs_common.h"
|
||||
#include "hs_control.h"
|
||||
#include "nodelist.h"
|
||||
//#include "rendcommon.h"
|
||||
//#include "rendservice.h"
|
||||
//#include "routerset.h"
|
||||
//#include "circuitbuild.h"
|
||||
#include "test_helpers.h"
|
||||
|
||||
/* mock ID digest and longname for node that's in nodelist */
|
||||
#define HSDIR_EXIST_ID \
|
||||
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
|
||||
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
|
||||
#define STR_HSDIR_EXIST_LONGNAME \
|
||||
"$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
|
||||
#define STR_HSDIR_NONE_EXIST_LONGNAME \
|
||||
"$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
|
||||
|
||||
/* Helper global variable for hidden service descriptor event test.
|
||||
* It's used as a pointer to dynamically created message buffer in
|
||||
* send_control_event_string_replacement function, which mocks
|
||||
* send_control_event_string function.
|
||||
*
|
||||
* Always free it after use! */
|
||||
static char *received_msg = NULL;
|
||||
|
||||
/** Mock function for send_control_event_string
|
||||
*/
|
||||
static void
|
||||
queue_control_event_string_replacement(uint16_t event, char *msg)
|
||||
{
|
||||
(void) event;
|
||||
tor_free(received_msg);
|
||||
received_msg = msg;
|
||||
}
|
||||
|
||||
/** Mock function for node_describe_longname_by_id, it returns either
|
||||
* STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
|
||||
*/
|
||||
static const char *
|
||||
node_describe_longname_by_id_replacement(const char *id_digest)
|
||||
{
|
||||
if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
|
||||
return STR_HSDIR_EXIST_LONGNAME;
|
||||
} else {
|
||||
return STR_HSDIR_NONE_EXIST_LONGNAME;
|
||||
}
|
||||
}
|
||||
|
||||
/* HSDir fetch index is a series of 'D' */
|
||||
#define HSDIR_INDEX_FETCH_HEX \
|
||||
"4343434343434343434343434343434343434343434343434343434343434343"
|
||||
#define HSDIR_INDEX_STORE_HEX \
|
||||
"4444444444444444444444444444444444444444444444444444444444444444"
|
||||
|
||||
static const node_t *
|
||||
mock_node_get_by_id(const char *digest)
|
||||
{
|
||||
static node_t node;
|
||||
memcpy(node.identity, digest, DIGEST_LEN);
|
||||
node.hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
|
||||
memset(node.hsdir_index->fetch, 'C', DIGEST256_LEN);
|
||||
memset(node.hsdir_index->store_first, 'D', DIGEST256_LEN);
|
||||
return &node;
|
||||
}
|
||||
|
||||
static void
|
||||
test_hs_desc_event(void *arg)
|
||||
{
|
||||
int ret;
|
||||
char *expected_msg = NULL;
|
||||
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
|
||||
ed25519_keypair_t identity_kp;
|
||||
ed25519_public_key_t blinded_pk;
|
||||
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
|
||||
routerstatus_t hsdir_rs;
|
||||
hs_ident_dir_conn_t ident;
|
||||
|
||||
(void) arg;
|
||||
MOCK(queue_control_event_string,
|
||||
queue_control_event_string_replacement);
|
||||
MOCK(node_describe_longname_by_id,
|
||||
node_describe_longname_by_id_replacement);
|
||||
MOCK(node_get_by_id, mock_node_get_by_id);
|
||||
|
||||
/* Setup what we need for this test. */
|
||||
ed25519_keypair_generate(&identity_kp, 0);
|
||||
hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
|
||||
ret = hs_address_is_valid(onion_address);
|
||||
tt_int_op(ret, OP_EQ, 1);
|
||||
memset(&blinded_pk, 'B', sizeof(blinded_pk));
|
||||
memset(&hsdir_rs, 0, sizeof(hsdir_rs));
|
||||
memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
|
||||
ret = ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
|
||||
tt_int_op(ret, OP_EQ, 0);
|
||||
memcpy(&ident.identity_pk, &identity_kp.pubkey,
|
||||
sizeof(ed25519_public_key_t));
|
||||
memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
|
||||
|
||||
/* HS_DESC REQUESTED ... */
|
||||
hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
|
||||
&hsdir_rs);
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH "
|
||||
STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX="
|
||||
HSDIR_INDEX_FETCH_HEX "\r\n",
|
||||
onion_address, base64_blinded_pk);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
/* HS_DESC CREATED... */
|
||||
hs_control_desc_event_created(onion_address, &blinded_pk);
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
|
||||
"UNKNOWN %s\r\n",
|
||||
onion_address, base64_blinded_pk);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
/* HS_DESC UPLOAD... */
|
||||
uint8_t hsdir_index_store[DIGEST256_LEN];
|
||||
memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
|
||||
hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
|
||||
&blinded_pk, hsdir_index_store);
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN "
|
||||
STR_HSDIR_EXIST_LONGNAME " %s "
|
||||
"HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n",
|
||||
onion_address, base64_blinded_pk);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
/* HS_DESC FAILED... */
|
||||
hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH "
|
||||
STR_HSDIR_EXIST_LONGNAME " %s "
|
||||
"REASON=BAD_DESC\r\n",
|
||||
onion_address, base64_blinded_pk);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
/* HS_DESC RECEIVED... */
|
||||
hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH "
|
||||
STR_HSDIR_EXIST_LONGNAME " %s\r\n",
|
||||
onion_address, base64_blinded_pk);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
/* HS_DESC UPLOADED... */
|
||||
hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
|
||||
tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
|
||||
STR_HSDIR_EXIST_LONGNAME "\r\n",
|
||||
onion_address);
|
||||
tt_assert(received_msg);
|
||||
tt_str_op(received_msg, OP_EQ, expected_msg);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
|
||||
done:
|
||||
UNMOCK(queue_control_event_string);
|
||||
UNMOCK(node_describe_longname_by_id);
|
||||
UNMOCK(node_get_by_id);
|
||||
tor_free(received_msg);
|
||||
tor_free(expected_msg);
|
||||
}
|
||||
|
||||
struct testcase_t hs_control_tests[] = {
|
||||
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
|
||||
NULL, NULL },
|
||||
|
||||
END_OF_TESTCASES
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user