Merge branch 'dgoulet_ticket20699_033_01'

This commit is contained in:
Nick Mathewson 2017-12-05 19:43:23 -05:00
commit e8a6a6635b
22 changed files with 1426 additions and 260 deletions

14
changes/ticket20699 Normal file
View 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.

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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 *

View File

@ -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);

View File

@ -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));

View File

@ -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
View 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
View 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) */

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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 \

View File

@ -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 },

View File

@ -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[];

View File

@ -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 },

View File

@ -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
View 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
};