From 05766eae3099ff1542b1dc4715b824d206b7c84f Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 7 Nov 2017 15:38:02 -0500 Subject: [PATCH 01/28] hs-v3: Add an encoded descriptor client cache lookup function This commit adds hs_cache_lookup_encoded_as_client() function that returns the encoded descriptor for a given service public key. This will be needed by the "GETINFO hs/client/desc/id/" control port command. Signed-off-by: David Goulet --- src/or/hs_cache.c | 18 ++++++++++++++++++ src/or/hs_cache.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/or/hs_cache.c b/src/or/hs_cache.c index 3ebe13fb4d..b864a0f717 100644 --- a/src/or/hs_cache.c +++ b/src/or/hs_cache.c @@ -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 key, 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 key, return * its HS descriptor if it's stored in our cache, or NULL if not. */ const hs_descriptor_t * diff --git a/src/or/hs_cache.h b/src/or/hs_cache.h index 2dcc518a71..a141634cc4 100644 --- a/src/or/hs_cache.h +++ b/src/or/hs_cache.h @@ -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); From e1d8e611c8fa1a3a1d3c2beb72c416c71f9cdf15 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 7 Nov 2017 15:42:38 -0500 Subject: [PATCH 02/28] control: Implement GETINFO hs/client/desc/id/ for HSv3 Part of #20699 Signed-off-by: David Goulet --- src/or/control.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 0d462b2d7d..193a65facc 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -58,6 +58,7 @@ #include "entrynodes.h" #include "geoip.h" #include "hibernate.h" +#include "hs_cache.h" #include "hs_common.h" #include "main.h" #include "microdesc.h" @@ -2014,20 +2015,46 @@ 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; From 0a3b2954487480114201758e02b3560e46ea1cca Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 7 Nov 2017 16:00:40 -0500 Subject: [PATCH 03/28] hs-v3: Add a lookup service current descriptor function This will be used by the control port command "GETINFO hs/service/desc/id/" which returns the encoded current descriptor for the given onion address. Signed-off-by: David Goulet --- src/or/hs_service.c | 25 +++++++++++++++++++++++++ src/or/hs_service.h | 2 ++ 2 files changed, 27 insertions(+) diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 8e2f52dcf0..349aa265d3 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -2900,6 +2900,31 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* 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) diff --git a/src/or/hs_service.h b/src/or/hs_service.h index ed1053d850..e78cc4c2b1 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -271,6 +271,8 @@ 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); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS From 660de600a036d0048fa6ba52bc2562f5a5ca6895 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 7 Nov 2017 16:02:00 -0500 Subject: [PATCH 04/28] control: Implement GETINFO hs/service/desc/id/ for HSv3 Part of #20699 Signed-off-by: David Goulet --- src/or/control.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 193a65facc..405c0c9357 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2057,20 +2057,47 @@ getinfo_helper_dir(control_connection_t *control_conn, } } } 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); From 49f21b6ba30a07369ff3282615465d0f3ad40e5b Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 9 Nov 2017 12:20:29 -0500 Subject: [PATCH 05/28] control: Support HSv3 interface for ADD_ONION At this commit, the key handling and generation is supported for a v3 service (ED25519-V3). However, the service creation is not yet implemented. This only adds the interface and code to deal with the new ED25519-V3 key type. Tests have been updated for RSA key type but nothing yet for ED25519-v3. Part of #20699 Signed-off-by: David Goulet --- src/or/control.c | 126 +++++++++++++++++++++++++++++++------ src/or/control.h | 9 +-- src/test/test_controller.c | 74 +++++++++++++--------- 3 files changed, 158 insertions(+), 51 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 405c0c9357..ef1e0edcd8 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4424,6 +4424,49 @@ 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 pk's type defers depending on the version so the + * caller should make sure those two matches. Both port_cfgs and auth_clients + * are now owned by the hidden service subsystem so the caller must stop + * accessing them. + * + * 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 rend_service_add_ephemeral_status_t +add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs, + int max_streams, int max_streams_close_circuit, + int auth_type, smartlist_t *auth_clients, + char **address_out) +{ + rend_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, port_cfgs, max_streams, + max_streams_close_circuit, auth_type, + auth_clients, address_out); + break; + } + case HS_VERSION_THREE: + { + /* XXX: Not implemented yet. */ + *address_out = tor_strdup("this is a v3 address"); + ret = RSAE_OKAY; + 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 @@ -4605,15 +4648,15 @@ handle_control_add_onion(control_connection_t *conn, } /* Parse the "keytype:keyblob" argument. */ - crypto_pk_t *pk = NULL; + int hs_version = 0; + void *pk = NULL; 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); @@ -4622,16 +4665,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) { @@ -4724,9 +4774,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, + void **decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4734,7 +4785,9 @@ 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; + + *decoded_key = NULL; smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { @@ -4746,6 +4799,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); @@ -4758,9 +4812,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 = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* "ED25519-V3:" - 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 = sk; + *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:" - Generating a new key, blob as algorithm. */ if (!strcasecmp(key_type_rsa1024, key_blob) || @@ -4774,12 +4842,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 = 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 = sk; + *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); goto err; @@ -4790,8 +4884,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } /* Succeded in loading or generating a private key. */ - tor_assert(pk); - ok = 1; + tor_assert(*decoded_key); + ret = 0; err: SMARTLIST_FOREACH(key_args, char *, cp, { @@ -4800,10 +4894,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 { @@ -4812,7 +4902,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 diff --git a/src/or/control.h b/src/or/control.h index 7ec182cb78..74601b9784 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -256,10 +256,11 @@ 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); +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, void **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); diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 472fcb8c53..056d9333fc 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -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" @@ -15,8 +16,9 @@ static void test_add_onion_helper_keyarg(void *arg) { + int ret, hs_version; + void *pk_ptr = NULL; crypto_pk_t *pk = NULL; - crypto_pk_t *pk2 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; @@ -26,38 +28,46 @@ test_add_onion_helper_keyarg(void *arg) (void) arg; /* 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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); 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_ptr); pk_ptr = 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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); 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_ptr); pk_ptr = 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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); /* 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_ptr); pk_ptr = NULL; + ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); @@ -67,41 +77,47 @@ test_add_onion_helper_keyarg(void *arg) pk = pk_generate(0); tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); - tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0); + tt_int_op(crypto_pk_cmp_keys(pk, pk_ptr), OP_EQ, 0); /* Test loading a invalid key type. */ tor_free(arg_str); crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk_ptr); pk_ptr = 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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); 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_ptr); pk_ptr = 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_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); 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(pk_ptr); tor_free(key_new_blob); tor_free(err_msg); tor_free(encoded); From 5d180309ea907711a98da82f07a48646699d03b1 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 9 Nov 2017 12:26:10 -0500 Subject: [PATCH 06/28] hs: Rename rend_service_add_ephemeral_status_t Move it to hs_common.h and rename it "hs_service_add_ephemeral_status_t". It will be shared between v2 and v3 services. Part of #20699 Signed-off-by: David Goulet --- src/or/control.c | 4 ++-- src/or/hs_common.h | 11 +++++++++++ src/or/rendservice.c | 4 ++-- src/or/rendservice.h | 11 +---------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index ef1e0edcd8..19527acb3c 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4433,13 +4433,13 @@ handle_control_hspost(control_connection_t *conn, * 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 rend_service_add_ephemeral_status_t +static hs_service_add_ephemeral_status_t add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs, int max_streams, int max_streams_close_circuit, int auth_type, smartlist_t *auth_clients, char **address_out) { - rend_service_add_ephemeral_status_t ret; + hs_service_add_ephemeral_status_t ret; tor_assert(pk); tor_assert(port_cfgs); diff --git a/src/or/hs_common.h b/src/or/hs_common.h index c95e59a6f8..58fcd1b93c 100644 --- a/src/or/hs_common.h +++ b/src/or/hs_common.h @@ -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 { diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 60234a5c1f..8eb5334908 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -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, diff --git a/src/or/rendservice.h b/src/or/rendservice.h index 5946e31861..15badce6ab 100644 --- a/src/or/rendservice.h +++ b/src/or/rendservice.h @@ -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, From f0e3331f3ca44aa8eb806487be2e03a2e9e52567 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 9 Nov 2017 14:28:22 -0500 Subject: [PATCH 07/28] hs-v3: Add ephemeral service support The functions are now used by the ADD_ONION/DEL_ONION control port command as well. This commits makes them fully functionnal with hidden service v3. Part of #20699 Signed-off-by: David Goulet --- src/or/control.c | 45 +++++++++---- src/or/hs_service.c | 126 +++++++++++++++++++++++++++++++++++++ src/or/hs_service.h | 6 ++ src/test/test_controller.c | 81 +++++++++++++++++++++++- 4 files changed, 243 insertions(+), 15 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 19527acb3c..d9102894e5 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4447,19 +4447,14 @@ add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs, switch (hs_version) { case HS_VERSION_TWO: - { ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, max_streams_close_circuit, auth_type, auth_clients, address_out); break; - } case HS_VERSION_THREE: - { - /* XXX: Not implemented yet. */ - *address_out = tor_strdup("this is a v3 address"); - ret = RSAE_OKAY; + ret = hs_service_add_ephemeral(pk, port_cfgs, max_streams, + max_streams_close_circuit, address_out); break; - } default: tor_assert_unreached(); } @@ -4971,6 +4966,7 @@ handle_control_del_onion(control_connection_t *conn, uint32_t len, const char *body) { + int hs_version = 0; smartlist_t *args; (void) len; /* body is nul-terminated; it's safe to ignore the length */ args = getargs_helper("DEL_ONION", conn, body, 1, 1); @@ -4978,7 +4974,11 @@ handle_control_del_onion(control_connection_t *conn, return 0; const char *service_id = smartlist_get(args, 0); - if (!rend_valid_v2_service_id(service_id)) { + if (rend_valid_v2_service_id(service_id)) { + hs_version = HS_VERSION_TWO; + } else if (hs_address_is_valid(service_id)) { + hs_version = HS_VERSION_THREE; + } else { connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n"); goto out; } @@ -5005,8 +5005,20 @@ handle_control_del_onion(control_connection_t *conn, if (onion_services == NULL) { connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n"); } else { - int ret = rend_service_del_ephemeral(service_id); - if (ret) { + int ret = -1; + switch (hs_version) { + case HS_VERSION_TWO: + ret = rend_service_del_ephemeral(service_id); + break; + case HS_VERSION_THREE: + ret = hs_service_del_ephemeral(service_id); + break; + default: + /* The ret value will be -1 thus hitting the warning below. This should + * never happen because of the check at the start of the function. */ + break; + } + if (ret < 0) { /* This should *NEVER* fail, since the service is on either the * per-control connection list, or the global one. */ @@ -5076,9 +5088,16 @@ connection_control_closed(control_connection_t *conn) * The list and it's contents are scrubbed/freed in connection_free_. */ if (conn->ephemeral_onion_services) { - SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, { - rend_service_del_ephemeral(cp); - }); + SMARTLIST_FOREACH_BEGIN(conn->ephemeral_onion_services, char *, cp) { + if (rend_valid_v2_service_id(cp)) { + rend_service_del_ephemeral(cp); + } else if (hs_address_is_valid(cp)) { + hs_service_del_ephemeral(cp); + } else { + /* An invalid .onion in our list should NEVER happen */ + tor_fragile_assert(); + } + } SMARTLIST_FOREACH_END(cp); } if (conn->is_owning_control_connection) { diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 349aa265d3..944400a0e5 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -2900,6 +2900,132 @@ service_add_fnames_to_list(const hs_service_t *service, smartlist_t *list) /* Public API */ /* ========== */ +/* Add the ephemeral service using the secret key sk and ports. Both max + * streams parameter will be set in the newly created service. + * + * Ownership of sk and ports is passed to this routine. Regardless of + * success/failure, callers should not touch these values after calling this + * routine, and may assume that correct cleanup has been done on failure. + * + * Return an appropriate hs_service_add_ephemeral_status_t. */ +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out) +{ + hs_service_add_ephemeral_status_t ret; + hs_service_t *service = NULL; + + tor_assert(sk); + tor_assert(ports); + tor_assert(address_out); + + service = hs_service_new(get_options()); + + /* Setup the service configuration with specifics. A default service is + * HS_VERSION_TWO so explicitely set it. */ + service->config.version = HS_VERSION_THREE; + service->config.max_streams_per_rdv_circuit = max_streams_per_rdv_circuit; + service->config.max_streams_close_circuit = !!max_streams_close_circuit; + service->config.is_ephemeral = 1; + smartlist_free(service->config.ports); + service->config.ports = ports; + + /* Handle the keys. */ + memcpy(&service->keys.identity_sk, sk, sizeof(service->keys.identity_sk)); + if (ed25519_public_key_generate(&service->keys.identity_pk, + &service->keys.identity_sk) < 0) { + log_warn(LD_CONFIG, "Unable to generate ed25519 public key" + "for v3 service."); + ret = RSAE_BADPRIVKEY; + goto err; + } + + /* Make sure we have at least one port. */ + if (smartlist_len(service->config.ports) == 0) { + log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified " + "for v3 service."); + ret = RSAE_BADVIRTPORT; + goto err; + } + + /* The only way the registration can fail is if the service public key + * already exists. */ + if (BUG(register_service(hs_service_map, service) < 0)) { + log_warn(LD_CONFIG, "Onion Service private key collides with an " + "existing v3 service."); + ret = RSAE_ADDREXISTS; + goto err; + } + + /* Last step is to build the onion address. */ + hs_build_address(&service->keys.identity_pk, + (uint8_t) service->config.version, + service->onion_address); + *address_out = tor_strdup(service->onion_address); + + log_info(LD_CONFIG, "Added ephemeral v3 onion service: %s", + safe_str_client(service->onion_address)); + ret = RSAE_OKAY; + goto end; + + err: + hs_service_free(service); + + end: + memwipe(sk, 0, sizeof(ed25519_secret_key_t)); + tor_free(sk); + return ret; +} + +/* For the given onion address, delete the ephemeral service. Return 0 on + * success else -1 on error. */ +int +hs_service_del_ephemeral(const char *address) +{ + uint8_t version; + ed25519_public_key_t pk; + hs_service_t *service = NULL; + + tor_assert(address); + + if (hs_parse_address(address, &pk, NULL, &version) < 0) { + log_warn(LD_CONFIG, "Requested malformed v3 onion address for removal."); + goto err; + } + + if (version != HS_VERSION_THREE) { + log_warn(LD_CONFIG, "Requested version of onion address for removal " + "is not supported."); + goto err; + } + + service = find_service(hs_service_map, &pk); + if (service == NULL) { + log_warn(LD_CONFIG, "Requested non-existent v3 hidden service for " + "removal."); + goto err; + } + + if (!service->config.is_ephemeral) { + log_warn(LD_CONFIG, "Requested non-ephemeral v3 hidden service for " + "removal."); + goto err; + } + + /* Close circuits, remove from map and finally free. */ + close_service_circuits(service); + remove_service(hs_service_map, service); + hs_service_free(service); + + log_info(LD_CONFIG, "Removed ephemeral v3 hidden service: %s", + safe_str_client(address)); + return 0; + + err: + return -1; +} + /* Using the ed25519 public key pk, find a service for that key and return the * current encoded descriptor as a newly allocated string or NULL if not * found. This is used by the control port subsystem. */ diff --git a/src/or/hs_service.h b/src/or/hs_service.h index e78cc4c2b1..de04987c8b 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -273,6 +273,12 @@ void hs_service_intro_circ_has_closed(origin_circuit_t *circ); char *hs_service_lookup_current_desc(const ed25519_public_key_t *pk); +hs_service_add_ephemeral_status_t +hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, + int max_streams_per_rdv_circuit, + int max_streams_close_circuit, char **address_out); +int hs_service_del_ephemeral(const char *address); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 056d9333fc..a5132bd4c9 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -14,7 +14,81 @@ #include "test_helpers.h" static void -test_add_onion_helper_keyarg(void *arg) +test_add_onion_helper_keyarg_v3(void *arg) +{ + int ret, hs_version; + void *pk_ptr = NULL; + char *key_new_blob = NULL; + char *err_msg = NULL; + const char *key_new_alg = NULL; + + (void) arg; + + /* Test explicit ED25519-V3 key generation. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg, + &key_new_blob, &pk_ptr, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk_ptr); + tt_str_op(key_new_alg, OP_EQ, "ED25519-V3"); + tt_assert(key_new_blob); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk_ptr); pk_ptr = NULL; + tor_free(key_new_blob); + + /* Test discarding the private key. */ + ret = add_onion_helper_keyarg("NEW:ED25519-V3", 1, &key_new_alg, + &key_new_blob, &pk_ptr, &hs_version, + &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk_ptr); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk_ptr); pk_ptr = NULL; + tor_free(key_new_blob); + + /* Test passing a key blob. */ + { + /* The base64 key and hex key are the same. Hex key is 64 bytes long. The + * sk has been generated randomly using python3. */ + const char *base64_sk = + "a9bT19PqGC9Y+BmOo1IQvCGjjwxMiaaxEXZ+FKMxpEQW" + "6AmSV5roThUGMRCaqQSCnR2jI1vL2QxHORzI4RxMmw=="; + const char *hex_sk = + "\x6b\xd6\xd3\xd7\xd3\xea\x18\x2f\x58\xf8\x19\x8e\xa3\x52\x10\xbc" + "\x21\xa3\x8f\x0c\x4c\x89\xa6\xb1\x11\x76\x7e\x14\xa3\x31\xa4\x44" + "\x16\xe8\x09\x92\x57\x9a\xe8\x4e\x15\x06\x31\x10\x9a\xa9\x04\x82" + "\x9d\x1d\xa3\x23\x5b\xcb\xd9\x0c\x47\x39\x1c\xc8\xe1\x1c\x4c\x9b"; + char *key_blob = NULL; + + tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); + tt_assert(key_blob); + ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, + &key_new_blob, &pk_ptr, &hs_version, + &err_msg); + tor_free(key_blob); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_THREE); + tt_assert(pk_ptr); + tt_mem_op(pk_ptr, OP_EQ, hex_sk, 64); + tt_ptr_op(key_new_alg, OP_EQ, NULL); + tt_ptr_op(key_new_blob, OP_EQ, NULL); + tt_ptr_op(err_msg, OP_EQ, NULL); + tor_free(pk_ptr); pk_ptr = NULL; + tor_free(key_new_blob); + } + + done: + tor_free(pk_ptr); + tor_free(key_new_blob); + tor_free(err_msg); +} + +static void +test_add_onion_helper_keyarg_v2(void *arg) { int ret, hs_version; void *pk_ptr = NULL; @@ -1386,7 +1460,10 @@ test_download_status_bridge(void *arg) } struct testcase_t controller_tests[] = { - { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL }, + { "add_onion_helper_keyarg_v2", test_add_onion_helper_keyarg_v2, 0, + NULL, NULL }, + { "add_onion_helper_keyarg_v3", test_add_onion_helper_keyarg_v3, 0, + NULL, NULL }, { "getinfo_helper_onion", test_getinfo_helper_onion, 0, NULL, NULL }, { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0, NULL, NULL }, From 4633a93ca841c36e9c36ae31bc6745c47f85767e Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 9 Nov 2017 14:30:20 -0500 Subject: [PATCH 08/28] hs-v3: Downgrade warning log when an intro circuit has closed When an intro circuit has closed, do not warn anymore when we can't find the service. It is possible to hit that condition if the service is removed before the circuits were fully closed. This happens in the case of deleting an ephemeral service. Signed-off-by: David Goulet --- src/or/hs_service.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 944400a0e5..a659a126fc 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -3079,7 +3079,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); From 242ddc85c874cafdc3513725f9357028b72eb9f8 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 09:08:05 -0500 Subject: [PATCH 09/28] control: Refactor HS_DESC events functions to not be v2 specific This is a naming refactor mostly _except_ for a the events' function that take a rend_data_t which will require much more refactoring. No behavior change at this commit, cleanup and renaming stuff to not be only v2 specific. Signed-off-by: David Goulet --- src/or/control.c | 63 +++++++++++++++++---------------------------- src/or/control.h | 15 ++++++----- src/or/rendclient.c | 3 ++- src/test/test_hs.c | 3 ++- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index d9102894e5..9a8d1f3021 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7177,25 +7177,21 @@ rend_hsaddress_str_or_unknown(const char *onion_address) * desc_id_base32 is the ID of requested hs descriptor. */ 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) { - 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); + if (BUG(!id_digest || !desc_id)) { return; } 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), + 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); } /** For an HS descriptor query rend_data, using the @@ -7244,52 +7240,45 @@ 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. * - * service_id is the descriptor onion address. - * desc_id_base32 is the descriptor ID. + * onion_address is service address. + * desc_id is the descriptor ID. * replica is the the descriptor replica number. */ 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); + if (BUG(!onion_address || !desc_id)) { return; } send_control_event(EVENT_HS_DESC, "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s " "REPLICA=%d\r\n", - service_id, - desc_id_base32, - replica); + onion_address, desc_id, replica); } /** send HS_DESC upload event. * - * service_id is the descriptor onion address. + * onion_address is service address. * hs_dir is the description of contacting hs directory. - * desc_id_base32 is the ID of requested hs descriptor. + * desc_id 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) { - 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); + if (BUG(!onion_address || !id_digest || !desc_id)) { return; } send_control_event(EVENT_HS_DESC, "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n", - service_id, + onion_address, node_describe_longname_by_id(id_digest), - desc_id_base32); + desc_id); } /** send HS_DESC event after got response from hs directory. @@ -7362,9 +7351,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; } @@ -7408,9 +7395,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; } @@ -7484,9 +7469,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, diff --git a/src/or/control.h b/src/or/control.h index 74601b9784..45fc4a9bc1 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -115,14 +115,15 @@ 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); +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, +void control_event_hs_descriptor_upload(const char *onion_address, + const char *desc_id, const char *hs_dir); void control_event_hs_descriptor_receive_end(const char *action, const char *onion_address, diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 3274819241..32a285457e 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -515,7 +515,8 @@ 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); return 1; diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 7737499f50..7f9eab9119 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -258,7 +258,8 @@ test_hs_desc_event(void *arg) sizeof(desc_id_base32)); /* test request event */ - control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID, + control_event_hs_descriptor_requested(rend_query.onion_address, + rend_query.auth_type, HSDIR_EXIST_ID, STR_DESC_ID_BASE32); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; From beacbbe2106f9af64a64fea3f69e5dcd43c1569e Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 11:16:16 -0500 Subject: [PATCH 10/28] control: Refactor control_event_hs_descriptor_receive_end First, rename and make that function static because it is internal to control.c and called by two HS_DESC events. Second, make it take more basic parameters and thus not a rend_data_t object so we can still use the function for v3 HS that doesn't use that object. Third, move the descriptor ID lookup to the two specific events (yes little code duplication there) because they get a rend_data_t object which won't be the case for v3. Finally, through this refactoring, change the pointer check to BUG() and change some parameter names to reflect what they really are. No behavior change at this commit. Signed-off-by: David Goulet --- src/or/control.c | 93 ++++++++++++++++++++++++++++-------------------- src/or/control.h | 5 --- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 9a8d1f3021..99acbcdcb2 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7289,33 +7289,20 @@ control_event_hs_descriptor_upload(const char *onion_address, * * 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); } @@ -7324,14 +7311,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); } @@ -7376,15 +7362,29 @@ control_event_hs_descriptor_upload_end(const char *action, void control_event_hs_descriptor_received(const char *onion_address, const rend_data_t *rend_data, - const char *id_digest) + 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 UPLOADED event @@ -7410,16 +7410,31 @@ control_event_hs_descriptor_uploaded(const char *id_digest, */ void control_event_hs_descriptor_failed(const rend_data_t *rend_data, - const char *id_digest, + 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_CONTENT event after completion of a successful fetch from hs diff --git a/src/or/control.h b/src/or/control.h index 45fc4a9bc1..a5f2599d22 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -125,11 +125,6 @@ void control_event_hs_descriptor_created(const char *onion_address, void control_event_hs_descriptor_upload(const char *onion_address, const char *desc_id, 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_end(const char *action, const char *onion_address, const char *hs_dir, From e7d606900e635719ed0cb5b76343a69ef4a1e43b Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 11:25:16 -0500 Subject: [PATCH 11/28] control: Rename two HS v2 specific functions Make control_event_hs_descriptor_received() and control_event_hs_descriptor_failed() v2 specific because they take a rend_data_t object and v3 will need to pass a different object. No behavior change. Signed-off-by: David Goulet --- src/or/control.c | 16 ++++++++-------- src/or/control.h | 13 +++++++------ src/or/directory.c | 12 ++++++------ src/or/rendclient.c | 5 +++-- src/test/test_hs.c | 10 +++++----- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 99acbcdcb2..e7ec238170 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7284,8 +7284,8 @@ control_event_hs_descriptor_upload(const char *onion_address, /** 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 * * So do not call this function directly. */ @@ -7360,9 +7360,9 @@ 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 *hsdir_id_digest) +control_event_hsv2_descriptor_received(const char *onion_address, + const rend_data_t *rend_data, + const char *hsdir_id_digest) { char *desc_id_field = NULL; const char *desc_id; @@ -7409,9 +7409,9 @@ 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 *hsdir_id_digest, - const char *reason) +control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, + const char *hsdir_id_digest, + const char *reason) { char *desc_id_field = NULL; const char *desc_id; diff --git a/src/or/control.h b/src/or/control.h index a5f2599d22..1744baba2e 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -129,14 +129,15 @@ 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); void control_event_hs_descriptor_upload_failed(const char *hs_dir, const char *onion_address, const char *reason); diff --git a/src/or/directory.c b/src/or/directory.c index 0c40b2018c..884ec2555e 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3136,9 +3136,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 +3173,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, diff --git a/src/or/rendclient.c b/src/or/rendclient.c index 32a285457e..eb097a50f6 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -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; diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 7f9eab9119..14799c9935 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -269,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); @@ -279,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 "\ @@ -290,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 "\ @@ -302,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"; From 743d0b9d91c0c26045aa9a725865870f0c052794 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 12:07:57 -0500 Subject: [PATCH 12/28] hs-v3: Implement HS_DESC REQUESTED event This changes the control_event_hs_descriptor_requested() call to add the hsdir index optional value. v2 passes NULL all the time. This commit creates hs_control.{c|h} that contains wrappers for the HS subsystem to interact with the control port subsystem. The descriptor REQUESTED event is implemented following proposal 284 extension for v3. Signed-off-by: David Goulet --- src/or/control.c | 16 +++++++++++--- src/or/control.h | 3 ++- src/or/hs_client.c | 5 +++++ src/or/hs_control.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_control.h | 18 ++++++++++++++++ src/or/include.am | 4 +++- src/or/rendclient.c | 2 +- src/test/test_hs.c | 2 +- 8 files changed, 95 insertions(+), 7 deletions(-) create mode 100644 src/or/hs_control.c create mode 100644 src/or/hs_control.h diff --git a/src/or/control.c b/src/or/control.c index e7ec238170..cd1be5bf4d 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7175,23 +7175,33 @@ rend_hsaddress_str_or_unknown(const char *onion_address) * rend_query is used to fetch requested onion address and auth type. * hs_dir is the description of contacting hs directory. * desc_id_base32 is the ID of requested hs descriptor. + * hsdir_index is the HSDir fetch index value for v3, an hex string. */ 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 *desc_id, + const char *hsdir_index) { + 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", + "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); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** For an HS descriptor query rend_data, using the diff --git a/src/or/control.h b/src/or/control.h index 1744baba2e..5a7a87c06f 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -118,7 +118,8 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest)); 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 *desc_id, + const char *hsdir_index); void control_event_hs_descriptor_created(const char *onion_address, const char *desc_id, int replica); diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 9ac653c721..666860155c 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -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)); diff --git a/src/or/hs_control.c b/src/or/hs_control.c new file mode 100644 index 0000000000..0bcb41dccf --- /dev/null +++ b/src/or/hs_control.c @@ -0,0 +1,52 @@ +/* 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 "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)); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h new file mode 100644 index 0000000000..2878ba5bca --- /dev/null +++ b/src/or/hs_control.h @@ -0,0 +1,18 @@ +/* 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 + +/* 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); + +#endif /* !defined(TOR_HS_CONTROL_H) */ + diff --git a/src/or/include.am b/src/or/include.am index b783f4855a..1c66cd2de3 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -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 \ diff --git a/src/or/rendclient.c b/src/or/rendclient.c index eb097a50f6..8291e5abfb 100644 --- a/src/or/rendclient.c +++ b/src/or/rendclient.c @@ -519,7 +519,7 @@ directory_get_from_hs_dir(const char *desc_id, 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; } diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 14799c9935..55c6218dd1 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -260,7 +260,7 @@ test_hs_desc_event(void *arg) /* test request event */ control_event_hs_descriptor_requested(rend_query.onion_address, rend_query.auth_type, HSDIR_EXIST_ID, - STR_DESC_ID_BASE32); + 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); From 8365de1da3de53fc02d463d78187625d16a5180b Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 14:01:33 -0500 Subject: [PATCH 13/28] hs-v3: Implement HS_DESC FAILED event A new v3 specific function has been added named control_event_hsv3_descriptor_failed(). The HS v3 subsystem now uses it. Signed-off-by: David Goulet --- src/or/control.c | 27 +++++++++++++++++++++++++++ src/or/control.h | 5 +++++ src/or/directory.c | 14 +++++++++++++- src/or/hs_control.c | 27 +++++++++++++++++++++++++++ src/or/hs_control.h | 7 +++++++ 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/or/control.c b/src/or/control.c index cd1be5bf4d..3ba3a4b3a0 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7296,6 +7296,7 @@ control_event_hs_descriptor_upload(const char *onion_address, * NOTE: this is an internal function used by following functions: * control_event_hsv2_descriptor_received * control_event_hsv2_descriptor_failed + * control_event_hsv3_descriptor_failed * * So do not call this function directly. */ @@ -7447,6 +7448,32 @@ control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, tor_free(desc_id_field); } +/** Send HS_DESC event to inform controller that the query to + * onion_address failed to retrieve hidden service descriptor + * desc_id from directory identified by hsdir_id_digest. If + * NULL, "UNKNOWN" is used. If reason 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 * directory. If hsdir_id_digest is NULL, it is replaced by "UNKNOWN". * If content is NULL, it is replaced by an empty string. The diff --git a/src/or/control.h b/src/or/control.h index 5a7a87c06f..4c9adb6818 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -139,6 +139,11 @@ void control_event_hsv2_descriptor_failed(const rend_data_t *rend_data, 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_hs_descriptor_upload_failed(const char *hs_dir, const char *onion_address, const char *reason); diff --git a/src/or/directory.c b/src/or/directory.c index 884ec2555e..1b6f7500b3 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -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,6 +3091,9 @@ 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"); } else { log_info(LD_REND, "Stored hidden service descriptor successfully."); TO_CONN(conn)->purpose = DIR_PURPOSE_HAS_FETCHED_HSDESC; @@ -3101,13 +3105,18 @@ 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"); 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"); break; default: log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " @@ -3115,6 +3124,9 @@ 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"); break; } diff --git a/src/or/hs_control.c b/src/or/hs_control.c index 0bcb41dccf..ed77c8e1d5 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -50,3 +50,30 @@ hs_control_desc_event_requested(const ed25519_public_key_t *onion_pk, 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); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index 2878ba5bca..fb8f3859f5 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -9,10 +9,17 @@ #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); + #endif /* !defined(TOR_HS_CONTROL_H) */ From 3b436d495fc6ad59ff5d9406d7ef17494286c299 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 14:12:34 -0500 Subject: [PATCH 14/28] hs-v3: Implement HS_DESC RECEIVED event Adds a v3 specific function to handle a received event. Signed-off-by: David Goulet --- src/or/control.c | 23 +++++++++++++++++++++++ src/or/control.h | 3 +++ src/or/directory.c | 2 ++ src/or/hs_control.c | 25 +++++++++++++++++++++++++ src/or/hs_control.h | 4 ++++ 5 files changed, 57 insertions(+) diff --git a/src/or/control.c b/src/or/control.c index 3ba3a4b3a0..a1515da05a 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7398,6 +7398,29 @@ control_event_hsv2_descriptor_received(const char *onion_address, 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 * * called when we successfully uploaded a hidden service descriptor. diff --git a/src/or/control.h b/src/or/control.h index 4c9adb6818..c11f6a07bd 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -144,6 +144,9 @@ 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); diff --git a/src/or/directory.c b/src/or/directory.c index 1b6f7500b3..a1d9c8c08a 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3098,6 +3098,8 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, 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); } break; case 404: diff --git a/src/or/hs_control.c b/src/or/hs_control.c index ed77c8e1d5..add62d5832 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -77,3 +77,28 @@ hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, 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); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index fb8f3859f5..33a96cec01 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -21,5 +21,9 @@ 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); + #endif /* !defined(TOR_HS_CONTROL_H) */ From b71a9b60cc0c29899637acc0610cb87c56869c1e Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 14:34:41 -0500 Subject: [PATCH 15/28] hs-v3: Implement HS_DESC CREATED event This makes the REPLICA= field optional for the control port event. A v2 service will always pass it and v3 is ignored. Signed-off-by: David Goulet --- src/or/control.c | 16 ++++++++++++---- src/or/hs_control.c | 23 +++++++++++++++++++++++ src/or/hs_control.h | 4 ++++ src/or/hs_service.c | 4 ++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index a1515da05a..15edc1f244 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7252,21 +7252,29 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp) * * onion_address is service address. * desc_id is the descriptor ID. - * replica is the the descriptor replica number. + * replica is the the descriptor replica number. If it is negative, it + * is ignored. */ void control_event_hs_descriptor_created(const char *onion_address, const char *desc_id, int replica) { + 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", - onion_address, desc_id, 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. diff --git a/src/or/hs_control.c b/src/or/hs_control.c index add62d5832..ea010605bb 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -102,3 +102,26 @@ hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, 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); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index 33a96cec01..3b117f19c3 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -25,5 +25,9 @@ void hs_control_desc_event_failed(const hs_ident_dir_conn_t *ident, 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); + #endif /* !defined(TOR_HS_CONTROL_H) */ diff --git a/src/or/hs_service.c b/src/or/hs_service.c index a659a126fc..65df5b2693 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -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: From c7050eaa16ca8c46c2cea81cc2f4d405ccf15968 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 14:48:52 -0500 Subject: [PATCH 16/28] hs-v3: Implement HS_DESC UPLOAD event Signed-off-by: David Goulet --- src/or/control.c | 15 ++++++++++++--- src/or/control.h | 3 ++- src/or/hs_control.c | 29 +++++++++++++++++++++++++++++ src/or/hs_control.h | 6 ++++++ src/or/hs_service.c | 5 ++++- src/or/rendservice.c | 4 ++-- 6 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 15edc1f244..0e41aab5e5 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -7286,17 +7286,26 @@ control_event_hs_descriptor_created(const char *onion_address, void control_event_hs_descriptor_upload(const char *onion_address, const char *id_digest, - const char *desc_id) + const char *desc_id, + const char *hsdir_index) { + 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", + "650 HS_DESC UPLOAD %s UNKNOWN %s %s%s\r\n", onion_address, node_describe_longname_by_id(id_digest), - desc_id); + desc_id, + hsdir_index_field ? hsdir_index_field : ""); + tor_free(hsdir_index_field); } /** send HS_DESC event after got response from hs directory. diff --git a/src/or/control.h b/src/or/control.h index c11f6a07bd..23f6eed8e5 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -125,7 +125,8 @@ void control_event_hs_descriptor_created(const char *onion_address, int replica); void control_event_hs_descriptor_upload(const char *onion_address, const char *desc_id, - const char *hs_dir); + 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, diff --git a/src/or/hs_control.c b/src/or/hs_control.c index ea010605bb..09dbbeba4b 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -125,3 +125,32 @@ hs_control_desc_event_created(const char *onion_address, 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)); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index 3b117f19c3..e10a300d77 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -29,5 +29,11 @@ void hs_control_desc_event_received(const hs_ident_dir_conn_t *ident, 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); + #endif /* !defined(TOR_HS_CONTROL_H) */ diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 65df5b2693..4f0465da40 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -2267,9 +2267,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; diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 8eb5334908..1e745d0fd6 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -3574,7 +3574,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; } } @@ -3629,7 +3629,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, From 427b247c8d67baf2382755a3855f8ea380068107 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 15:00:18 -0500 Subject: [PATCH 17/28] hs-v3: Implement HS_DESC UPLOADED event Signed-off-by: David Goulet --- src/or/directory.c | 2 +- src/or/hs_control.c | 18 ++++++++++++++++++ src/or/hs_control.h | 4 ++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/or/directory.c b/src/or/directory.c index a1d9c8c08a..cbd7af075c 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3306,7 +3306,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, diff --git a/src/or/hs_control.c b/src/or/hs_control.c index 09dbbeba4b..fce6466ef1 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -154,3 +154,21 @@ hs_control_desc_event_upload(const char *onion_address, 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); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index e10a300d77..de23dc73d8 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -35,5 +35,9 @@ void hs_control_desc_event_upload(const char *onion_address, 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); + #endif /* !defined(TOR_HS_CONTROL_H) */ From 13bb4f60f0dc390ac92c8963bbf32ee55187d7b5 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 15:08:05 -0500 Subject: [PATCH 18/28] hs-v3: Support HS_DESC UPLOAD failed When failing to upload a descriptor, signal the control port with a FAILED event. Signed-off-by: David Goulet --- src/or/directory.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/or/directory.c b/src/or/directory.c index cbd7af075c..ae8bf6eb05 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3314,7 +3314,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 " @@ -3322,7 +3323,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; } From cc26d4fa2131e056dc7728335d877e0322611b12 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Fri, 10 Nov 2017 15:18:35 -0500 Subject: [PATCH 19/28] control: Support HS v3 for CIRC and CIRC_MINOR event "REND_QUERY=" can now output a v3 address. Signed-off-by: David Goulet --- src/or/control.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 0e41aab5e5..24e3ebe7dd 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -2678,9 +2678,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); } { From 2c8e97db587df4c3145ab98dd9251290be7a3379 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 14 Nov 2017 11:06:35 -0500 Subject: [PATCH 20/28] hs-v3: Implement HS_DESC_CONTENT event Signed-off-by: David Goulet --- src/or/directory.c | 10 ++++++++++ src/or/hs_control.c | 27 +++++++++++++++++++++++++++ src/or/hs_control.h | 5 +++++ 3 files changed, 42 insertions(+) diff --git a/src/or/directory.c b/src/or/directory.c index ae8bf6eb05..1a7c016c3e 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -3094,12 +3094,16 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* 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: @@ -3110,6 +3114,8 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* 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: " @@ -3119,6 +3125,8 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* 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: " @@ -3129,6 +3137,8 @@ handle_response_fetch_hsdesc_v3(dir_connection_t *conn, /* 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; } diff --git a/src/or/hs_control.c b/src/or/hs_control.c index fce6466ef1..9a05d936b6 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -172,3 +172,30 @@ hs_control_desc_event_uploaded(const hs_ident_dir_conn_t *ident, 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); +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index de23dc73d8..35062d4b81 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -39,5 +39,10 @@ void hs_control_desc_event_upload(const char *onion_address, 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); + #endif /* !defined(TOR_HS_CONTROL_H) */ From e71c6199ddd9514f6f04c13f4393bfc28961b272 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 15 Nov 2017 14:01:16 -0500 Subject: [PATCH 21/28] hs-v3: Add a public function to upload a descriptor to an HSDir This is groundwork for the HSPOST control port command that needs a way in the HS subsystem to upload a service descriptor to a specific HSDir. To do so, we add a public function that takes a series of parameters including a fully encoded descriptor and initiate a directory request to a specific routerstatut_t object. It is for now not used but should be, in future commit, by the HSPOST command. This commit has no behavior change, only refactoring. Signed-off-by: David Goulet --- src/or/hs_service.c | 81 +++++++++++++++++++++++++++++---------------- src/or/hs_service.h | 7 ++++ 2 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 4f0465da40..f65b59ae1c 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -2203,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. " @@ -2228,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//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); @@ -2907,6 +2884,54 @@ 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//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. * diff --git a/src/or/hs_service.h b/src/or/hs_service.h index de04987c8b..678f24b0a2 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -279,6 +279,13 @@ hs_service_add_ephemeral(ed25519_secret_key_t *sk, smartlist_t *ports, 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 From 683fccba8fb4543867435bd83851da821cd7f10e Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 15 Nov 2017 14:34:53 -0500 Subject: [PATCH 22/28] hs-v3: Add an handler for the HSPOST command It is not used yet at this commit. Signed-off-by: David Goulet --- src/or/hs_control.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_control.h | 4 ++++ 2 files changed, 59 insertions(+) diff --git a/src/or/hs_control.c b/src/or/hs_control.c index 9a05d936b6..1b1fe78575 100644 --- a/src/or/hs_control.c +++ b/src/or/hs_control.c @@ -10,6 +10,8 @@ #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. @@ -199,3 +201,56 @@ hs_control_desc_event_content(const hs_ident_dir_conn_t *ident, 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; +} + diff --git a/src/or/hs_control.h b/src/or/hs_control.h index 35062d4b81..95c46e655e 100644 --- a/src/or/hs_control.h +++ b/src/or/hs_control.h @@ -44,5 +44,9 @@ 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) */ From 475d8d1a3d2e68c237095b88417f756afc360147 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 15 Nov 2017 14:37:11 -0500 Subject: [PATCH 23/28] control: Don't check if Server is an HSDir for HSPOST This is removed for two reasons. First, HSDir accepts descriptor even though they don't think they are in fact an HSDir. This is to avoid consensus desync between client/service and directories. Second, our malicious HSDir scanner uses the HSPOST command to post on all relays in order to test them before they could become HSDir. We had to remove that check from the tor code that the scanner uses. Thus, this check should not be enforced by the control port for the above use cases. It is also a bit more complex with v3 support for which not all HSDir support it so basically irrelevant check. Signed-off-by: David Goulet --- src/or/control.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index 24e3ebe7dd..7ade90ea8b 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4370,11 +4370,6 @@ 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(); From ace42acff0d658fb39ce38d0502ef68b1bfa67c8 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Wed, 15 Nov 2017 14:52:24 -0500 Subject: [PATCH 24/28] control: HSPOST command support for v3 Signed-off-by: David Goulet --- src/or/control.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/or/control.c b/src/or/control.c index 7ade90ea8b..dc045c39a4 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -60,6 +60,7 @@ #include "hibernate.h" #include "hs_cache.h" #include "hs_common.h" +#include "hs_control.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -4338,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) { @@ -4374,6 +4377,12 @@ handle_control_hspost(control_connection_t *conn, 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); @@ -4382,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)); From 9c6560fe29c9e2ee33549ca890f94c03cbdd94a8 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Mon, 20 Nov 2017 12:10:07 -0500 Subject: [PATCH 25/28] test: Add HS_DESC v3 unit tests This introduces the test_hs_control.c file which at this commit contains basic unit test for the HS_DESC event. Signed-off-by: David Goulet --- src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_hs_control.c | 199 +++++++++++++++++++++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 src/test/test_hs_control.c diff --git a/src/test/include.am b/src/test/include.am index bbf8a370e2..b5c5dd24f7 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -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 \ diff --git a/src/test/test.c b/src/test/test.c index 00857c2386..432df35c34 100644 --- a/src/test/test.c +++ b/src/test/test.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 }, diff --git a/src/test/test.h b/src/test/test.h index b1a3366a80..b37fe25b33 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -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[]; diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c new file mode 100644 index 0000000000..207a55de6d --- /dev/null +++ b/src/test/test_hs_control.c @@ -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 +}; + From 8c02fc15ae8391d800926c0c6df7fb258139ce79 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 5 Dec 2017 14:24:00 -0500 Subject: [PATCH 26/28] control: Don't use void pointer for ADD_ONION secret key Make this a bit more safe with at least type checking of the pointers depending on the version. Signed-off-by: David Goulet --- src/or/control.c | 30 +++++++------- src/or/control.h | 13 +++++- src/test/test_controller.c | 81 ++++++++++++++++++++------------------ 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index dc045c39a4..d84ce7d639 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4458,10 +4458,11 @@ handle_control_hspost(control_connection_t *conn, * 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, void *pk, smartlist_t *port_cfgs, - int max_streams, int max_streams_close_circuit, - int auth_type, smartlist_t *auth_clients, - char **address_out) +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; @@ -4471,12 +4472,12 @@ add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs, switch (hs_version) { case HS_VERSION_TWO: - ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, + 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, port_cfgs, max_streams, + ret = hs_service_add_ephemeral(pk->v3, port_cfgs, max_streams, max_streams_close_circuit, address_out); break; default: @@ -4668,7 +4669,7 @@ handle_control_add_onion(control_connection_t *conn, /* Parse the "keytype:keyblob" argument. */ int hs_version = 0; - void *pk = NULL; + add_onion_secret_key_t pk; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; @@ -4697,7 +4698,7 @@ handle_control_add_onion(control_connection_t *conn, * regardless of success/failure. */ char *service_id = NULL; - int ret = add_onion_helper_add_service(hs_version, pk, port_cfgs, + int ret = add_onion_helper_add_service(hs_version, &pk, port_cfgs, max_streams, max_streams_close_circuit, auth_type, auth_clients, &service_id); @@ -4796,7 +4797,7 @@ handle_control_add_onion(control_connection_t *conn, STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, - void **decoded_key, int *hs_version, + add_onion_secret_key_t *decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4806,8 +4807,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, char *err_msg = NULL; int ret = -1; - *decoded_key = NULL; - smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { err_msg = tor_strdup("512 Invalid key type/blob\r\n"); @@ -4835,7 +4834,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, err_msg = tor_strdup("512 Invalid RSA key size\r\n"); goto err; } - *decoded_key = pk; + decoded_key->v2 = pk; *hs_version = HS_VERSION_TWO; } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { /* "ED25519-V3:" - Loading a pre-existing ed25519 key. */ @@ -4846,7 +4845,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); goto err; } - *decoded_key = sk; + decoded_key->v3 = sk; *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:" - Generating a new key, blob as algorithm. */ @@ -4868,7 +4867,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } key_new_alg = key_type_rsa1024; } - *decoded_key = pk; + 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)); @@ -4891,7 +4890,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } key_new_alg = key_type_ed25519_v3; } - *decoded_key = sk; + decoded_key->v3 = sk; *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); @@ -4903,7 +4902,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } /* Succeded in loading or generating a private key. */ - tor_assert(*decoded_key); ret = 0; err: diff --git a/src/or/control.h b/src/or/control.h index 23f6eed8e5..28ffeaed86 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -263,9 +263,20 @@ void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void); +/* 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, void **decoded_key, + char **key_new_blob_out, + add_onion_secret_key_t *decoded_key, int *hs_version, char **err_msg_out); STATIC rend_authorized_client_t * diff --git a/src/test/test_controller.c b/src/test/test_controller.c index a5132bd4c9..af19f63f6c 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -17,37 +17,39 @@ static void test_add_onion_helper_keyarg_v3(void *arg) { int ret, hs_version; - void *pk_ptr = NULL; + 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_ptr, &hs_version, + &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_ptr); + 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_ptr); pk_ptr = 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_ptr, &hs_version, + &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_ptr); + 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_ptr); pk_ptr = NULL; + tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); /* Test passing a key blob. */ @@ -67,22 +69,22 @@ test_add_onion_helper_keyarg_v3(void *arg) tor_asprintf(&key_blob, "ED25519-V3:%s", base64_sk); tt_assert(key_blob); ret = add_onion_helper_keyarg(key_blob, 1, &key_new_alg, - &key_new_blob, &pk_ptr, &hs_version, + &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_ptr); - tt_mem_op(pk_ptr, OP_EQ, hex_sk, 64); + 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_ptr); pk_ptr = NULL; + tor_free(pk.v3); pk.v3 = NULL; tor_free(key_new_blob); } done: - tor_free(pk_ptr); + tor_free(pk.v3); tor_free(key_new_blob); tor_free(err_msg); } @@ -91,8 +93,8 @@ static void test_add_onion_helper_keyarg_v2(void *arg) { int ret, hs_version; - void *pk_ptr = NULL; - crypto_pk_t *pk = NULL; + 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; @@ -101,97 +103,100 @@ test_add_onion_helper_keyarg_v2(void *arg) (void) arg; + memset(&pk, 0, sizeof(pk)); + /* Test explicit RSA1024 key generation. */ ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &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_ptr); + 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_ptr); pk_ptr = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &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_ptr); + 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_ptr); pk_ptr = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_free(key_new_blob); ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &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_ptr); + 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_ptr); pk_ptr = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &pk, &hs_version, &err_msg); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); - tt_ptr_op(pk_ptr, OP_EQ, NULL); + 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); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &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_ptr); + 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, pk_ptr), 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(pk_ptr); pk_ptr = NULL; + crypto_pk_free(pk1); pk1 = NULL; + crypto_pk_free(pk.v2); pk.v2 = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &pk, &hs_version, &err_msg); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); - tt_ptr_op(pk_ptr, OP_EQ, NULL); + 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_ptr); pk_ptr = 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); ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &pk_ptr, &hs_version, &err_msg); + &pk, &hs_version, &err_msg); tt_int_op(ret, OP_EQ, -1); tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); - tt_ptr_op(pk_ptr, OP_EQ, NULL); + 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_ptr); + crypto_pk_free(pk1); + crypto_pk_free(pk.v2); tor_free(key_new_blob); tor_free(err_msg); tor_free(encoded); From c094802697fe10a3f24dbb3f506c51a78f5c7a4f Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 5 Dec 2017 14:30:47 -0500 Subject: [PATCH 27/28] control: Improve ADD_ONION helper function comments Signed-off-by: David Goulet --- src/or/control.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/or/control.c b/src/or/control.c index d84ce7d639..f1960b8849 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4449,10 +4449,17 @@ handle_control_hspost(control_connection_t *conn, } /* Helper function for ADD_ONION that adds an ephemeral service depending on - * the given hs_version. The pk's type defers depending on the version so the - * caller should make sure those two matches. Both port_cfgs and auth_clients - * are now owned by the hidden service subsystem so the caller must stop - * accessing them. + * 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 From 5d4fc193f911d669151f102e5597438e8219ea81 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 5 Dec 2017 14:54:00 -0500 Subject: [PATCH 28/28] control: Add changes file for HSv3 control port Part of #20699. Signed-off-by: David Goulet --- changes/ticket20699 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 changes/ticket20699 diff --git a/changes/ticket20699 b/changes/ticket20699 new file mode 100644 index 0000000000..a93236ba40 --- /dev/null +++ b/changes/ticket20699 @@ -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.