tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
August 2017
- 17 participants
- 1132 discussions

[tor/master] prop224: Refactor rendclient.c to use the new hsdir_req code.
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 5c9cd912eece244f90ecf31712722dc6b993f6da
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Thu Jun 1 14:01:48 2017 +0300
prop224: Refactor rendclient.c to use the new hsdir_req code.
- Also add tests for the hidserv_req subsystem.
- Introduce purge_v2_hidserv_req() wrapper to simplify v2 code.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/rendclient.c | 21 ++++++++--
src/test/test_hs_common.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 117 insertions(+), 4 deletions(-)
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index a621d27f9..9cf405946 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -562,6 +562,20 @@ directory_get_from_hs_dir(const char *desc_id,
return 1;
}
+/** Remove tracked HSDir requests from our history for this hidden service
+ * descriptor <b>desc_id</b> (of size DIGEST_LEN) */
+static void
+purge_v2_hidserv_req(const char *desc_id)
+{
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+
+ /* The hsdir request tracker stores v2 keys using the base32 encoded
+ desc_id. Do it: */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ hs_purge_hid_serv_from_last_hid_serv_requests(desc_id_base32);
+}
+
/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are
* given, they will be used instead.
*
@@ -636,8 +650,7 @@ fetch_v2_desc_by_addr(rend_data_t *rend_query, smartlist_t *hsdirs)
sizeof(descriptor_id)) != 0) {
/* Not equal from what we currently have so purge the last hid serv
* request cache and update the descriptor ID with the new value. */
- hs_purge_hid_serv_from_last_hid_serv_requests(
- rend_data->descriptor_id[chosen_replica]);
+ purge_v2_hidserv_req(rend_data->descriptor_id[chosen_replica]);
memcpy(rend_data->descriptor_id[chosen_replica], descriptor_id,
sizeof(rend_data->descriptor_id[chosen_replica]));
}
@@ -1036,14 +1049,14 @@ rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
for (replica = 0; replica < ARRAY_LENGTH(rend_data_v2->descriptor_id);
replica++) {
const char *desc_id = rend_data_v2->descriptor_id[replica];
- hs_purge_hid_serv_from_last_hid_serv_requests(desc_id);
+ purge_v2_hidserv_req(desc_id);
}
log_info(LD_REND, "Connection attempt for %s has ended; "
"cleaning up temporary state.",
safe_str_client(onion_address));
} else {
/* We only have an ID for a fetch. Probably used by HSFETCH. */
- hs_purge_hid_serv_from_last_hid_serv_requests(rend_data_v2->desc_id_fetch);
+ purge_v2_hidserv_req(rend_data_v2->desc_id_fetch);
}
}
diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c
index f7e8e2811..b63cb7c94 100644
--- a/src/test/test_hs_common.c
+++ b/src/test/test_hs_common.c
@@ -697,6 +697,104 @@ test_disaster_srv(void *arg)
;
}
+/** Test our HS descriptor request tracker by making various requests and
+ * checking whether they get tracked properly. */
+static void
+test_hid_serv_request_tracker(void *arg)
+{
+ (void) arg;
+ time_t retval;
+ routerstatus_t *hsdir = NULL, *hsdir2 = NULL;
+ time_t now = approx_time();
+
+ const char *req_key_str_first =
+ "vd4zb6zesaubtrjvdqcr2w7x7lhw2up4Xnw4526ThUNbL5o1go+EdUuEqlKxHkNbnK41pRzizzs";
+ const char *req_key_str_second =
+ "g53o7iavcd62oihswhr24u6czmqws5kpXnw4526ThUNbL5o1go+EdUuEqlKxHkNbnK41pRzizzs";
+
+ /*************************** basic test *******************************/
+
+ /* Get request tracker and make sure it's empty */
+ strmap_t *request_tracker = get_last_hid_serv_requests();
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 0);
+
+ /* Let's register a hid serv request */
+ hsdir = tor_malloc_zero(sizeof(routerstatus_t));
+ memset(hsdir->identity_digest, 'Z', DIGEST_LEN);
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now, 1);
+ tt_int_op(retval, OP_EQ, now);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /* Let's lookup a non-existent hidserv request */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_second,
+ now+1, 0);
+ tt_int_op(retval, OP_EQ, 0);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /* Let's lookup a real hidserv request */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now+2, 0);
+ tt_int_op(retval, OP_EQ, now); /* we got it */
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /**********************************************************************/
+
+ /* Let's add another request for the same HS but on a different HSDir. */
+ hsdir2 = tor_malloc_zero(sizeof(routerstatus_t));
+ memset(hsdir->identity_digest, 2, DIGEST_LEN);
+ retval = hs_lookup_last_hid_serv_request(hsdir2, req_key_str_first,
+ now+3, 1);
+ tt_int_op(retval, OP_EQ, now+3);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+
+ /* Check that we can clean the first request based on time */
+ hs_clean_last_hid_serv_requests(now+3+REND_HID_SERV_DIR_REQUERY_PERIOD);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+ /* Check that it doesn't exist anymore */
+ retval = hs_lookup_last_hid_serv_request(hsdir, req_key_str_first,
+ now+2, 0);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /*************************** deleting entries **************************/
+
+ /* Add another request with very short key */
+ retval = hs_lookup_last_hid_serv_request(hsdir, "l", now, 1);
+
+ /* Try deleting entries with a dummy key. Check that our previous requests
+ * are still there */
+ tor_capture_bugs_(1);
+ hs_purge_hid_serv_from_last_hid_serv_requests("a");
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+ tor_end_capture_bugs_();
+
+ /* Try another dummy key. Check that requests are still there */
+ {
+ char dummy[2000];
+ memset(dummy, 'Z', 2000);
+ dummy[1999] = '\x00';
+ hs_purge_hid_serv_from_last_hid_serv_requests(dummy);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+ }
+
+ /* Another dummy key! */
+ hs_purge_hid_serv_from_last_hid_serv_requests(req_key_str_second);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 2);
+
+ /* Now actually delete a request! */
+ hs_purge_hid_serv_from_last_hid_serv_requests(req_key_str_first);
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 1);
+
+ /* Purge it all! */
+ hs_purge_last_hid_serv_requests();
+ request_tracker = get_last_hid_serv_requests();
+ tt_int_op(strmap_size(request_tracker),OP_EQ, 0);
+
+ done:
+ tor_free(hsdir);
+ tor_free(hsdir2);
+}
+
static void
test_parse_extended_hostname(void *arg)
{
@@ -748,6 +846,8 @@ struct testcase_t hs_common_tests[] = {
NULL, NULL },
{ "disaster_srv", test_disaster_srv, TT_FORK,
NULL, NULL },
+ { "hid_serv_request_tracker", test_hid_serv_request_tracker, TT_FORK,
+ NULL, NULL },
{ "parse_extended_hostname", test_parse_extended_hostname, TT_FORK,
NULL, NULL },
1
0

[tor/master] test: Add tests for fetching descs and handling SOCKS conns.
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 6eb9de1b8c2ede739ebcd3514201c07365fadb18
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Thu Jun 1 14:35:39 2017 +0300
test: Add tests for fetching descs and handling SOCKS conns.
- Add tests that ensure that SOCKS requests for v2/v3 addresses get
intercepted and handled.
- Add test that stores and lookups an HS descriptor in the client-side cache.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/directory.c | 10 ------
src/or/directory.h | 12 ++++++-
src/test/test_entryconn.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++
src/test/test_hs_cache.c | 66 +++++++++++++++++++++++++++++++++++
4 files changed, 164 insertions(+), 11 deletions(-)
diff --git a/src/or/directory.c b/src/or/directory.c
index 698ee9013..9268c3ca2 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2203,16 +2203,6 @@ load_downloaded_routers(const char *body, smartlist_t *which,
return added;
}
-/** A structure to hold arguments passed into each directory response
- * handler */
-typedef struct response_handler_args_t {
- int status_code;
- const char *reason;
- const char *body;
- size_t body_len;
- const char *headers;
-} response_handler_args_t;
-
static int handle_response_fetch_consensus(dir_connection_t *,
const response_handler_args_t *);
static int handle_response_fetch_certificate(dir_connection_t *,
diff --git a/src/or/directory.h b/src/or/directory.h
index 92d9b266d..1acdff346 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -168,6 +168,16 @@ int purpose_needs_anonymity(uint8_t dir_purpose, uint8_t router_purpose,
#ifdef DIRECTORY_PRIVATE
+/** A structure to hold arguments passed into each directory response
+ * handler */
+typedef struct response_handler_args_t {
+ int status_code;
+ const char *reason;
+ const char *body;
+ size_t body_len;
+ const char *headers;
+} response_handler_args_t;
+
struct get_handler_args_t;
STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn,
const struct get_handler_args_t *args);
@@ -183,7 +193,7 @@ STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
#endif
#ifdef TOR_UNIT_TESTS
-/* Used only by test_dir.c */
+/* Used only by test_dir.c and test_hs_cache.c */
STATIC int parse_http_url(const char *headers, char **url);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c
index 12a631630..9fe3db26f 100644
--- a/src/test/test_entryconn.c
+++ b/src/test/test_entryconn.c
@@ -14,6 +14,10 @@
#include "confparse.h"
#include "connection.h"
#include "connection_edge.h"
+#include "nodelist.h"
+
+#include "hs_cache.h"
+#include "rendcache.h"
static void *
entryconn_rewrite_setup(const struct testcase_t *tc)
@@ -743,6 +747,87 @@ test_entryconn_rewrite_mapaddress_automap_onion4(void *arg)
test_entryconn_rewrite_mapaddress_automap_onion_common(arg, 0, 1);
}
+/** Test that rewrite functions can handle v2 addresses */
+static void
+test_entryconn_rewrite_onion_v2(void *arg)
+{
+ int retval;
+ entry_connection_t *conn = arg;
+
+ (void) arg;
+
+ rend_cache_init();
+
+ /* Make a SOCKS request */
+ conn->socks_request->command = SOCKS_COMMAND_CONNECT;
+ strlcpy(conn->socks_request->address,
+ "pqeed46efnwmfuid.onion",
+ sizeof(conn->socks_request->address));
+
+ /* Make an onion connection using the SOCKS request */
+ conn->entry_cfg.onion_traffic = 1;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_SOCKS_WAIT;
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+
+ /* Handle SOCKS and rewrite! */
+ retval = connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check connection state after rewrite */
+ tt_int_op(ENTRY_TO_CONN(conn)->state, OP_EQ, AP_CONN_STATE_RENDDESC_WAIT);
+ /* check that the address got rewritten */
+ tt_str_op(conn->socks_request->address, OP_EQ,
+ "pqeed46efnwmfuid");
+ /* check that HS information got attached to the connection */
+ tt_assert(ENTRY_TO_EDGE_CONN(conn)->rend_data);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+
+ done:
+ rend_cache_free_all();
+ /* 'conn' is cleaned by handler */
+}
+
+/** Test that rewrite functions can handle v3 onion addresses */
+static void
+test_entryconn_rewrite_onion_v3(void *arg)
+{
+ int retval;
+ entry_connection_t *conn = arg;
+
+ (void) arg;
+
+ hs_cache_init();
+
+ /* Make a SOCKS request */
+ conn->socks_request->command = SOCKS_COMMAND_CONNECT;
+ strlcpy(conn->socks_request->address,
+ "git.p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad.onion",
+ sizeof(conn->socks_request->address));
+
+ /* Make an onion connection using the SOCKS request */
+ conn->entry_cfg.onion_traffic = 1;
+ ENTRY_TO_CONN(conn)->state = AP_CONN_STATE_SOCKS_WAIT;
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+
+ /* Handle SOCKS and rewrite! */
+ retval = connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
+ tt_int_op(retval, OP_EQ, 0);
+
+ /* Check connection state after rewrite */
+ tt_int_op(ENTRY_TO_CONN(conn)->state, OP_EQ, AP_CONN_STATE_RENDDESC_WAIT);
+ /* check that the address got rewritten */
+ tt_str_op(conn->socks_request->address, OP_EQ,
+ "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad");
+ /* check that HS information got attached to the connection */
+ tt_assert(ENTRY_TO_EDGE_CONN(conn)->hs_ident);
+ tt_assert(!ENTRY_TO_EDGE_CONN(conn)->rend_data);
+
+ done:
+ hs_free_all();
+ /* 'conn' is cleaned by handler */
+}
+
#define REWRITE(name) \
{ #name, test_entryconn_##name, TT_FORK, &test_rewrite_setup, NULL }
@@ -763,6 +848,8 @@ struct testcase_t entryconn_tests[] = {
REWRITE(rewrite_mapaddress_automap_onion2),
REWRITE(rewrite_mapaddress_automap_onion3),
REWRITE(rewrite_mapaddress_automap_onion4),
+ REWRITE(rewrite_onion_v2),
+ REWRITE(rewrite_onion_v3),
END_OF_TESTCASES
};
diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c
index 6c2addef9..d3950c0c2 100644
--- a/src/test/test_hs_cache.c
+++ b/src/test/test_hs_cache.c
@@ -7,6 +7,7 @@
*/
#define CONNECTION_PRIVATE
+#define DIRECTORY_PRIVATE
#define HS_CACHE_PRIVATE
#include "ed25519_cert.h"
@@ -431,6 +432,69 @@ test_hsdir_revision_counter_check(void *arg)
tor_free(published_desc_str);
}
+/** Test that we can store HS descriptors in the client HS cache. */
+static void
+test_client_cache(void *arg)
+{
+ int retval;
+ ed25519_keypair_t signing_kp;
+ hs_descriptor_t *published_desc = NULL;
+ char *published_desc_str = NULL;
+
+ response_handler_args_t *args = NULL;
+ dir_connection_t *conn = NULL;
+
+ (void) arg;
+
+ /* Initialize HSDir cache subsystem */
+ init_test();
+
+ /* Generate a valid descriptor with normal values. */
+ {
+ retval = ed25519_keypair_generate(&signing_kp, 0);
+ tt_int_op(retval, ==, 0);
+ published_desc = hs_helper_build_hs_desc_with_ip(&signing_kp);
+ tt_assert(published_desc);
+ retval = hs_desc_encode_descriptor(published_desc, &signing_kp,
+ &published_desc_str);
+ tt_int_op(retval, OP_EQ, 0);
+ }
+
+ /* Test handle_response_fetch_hsdesc_v3() */
+ {
+ args = tor_malloc_zero(sizeof(response_handler_args_t));
+ args->status_code = 200;
+ args->reason = NULL;
+ args->body = published_desc_str;
+ args->body_len = strlen(published_desc_str);
+
+ conn = tor_malloc_zero(sizeof(dir_connection_t));
+ conn->hs_ident = tor_malloc_zero(sizeof(hs_ident_dir_conn_t));
+ ed25519_pubkey_copy(&conn->hs_ident->identity_pk, &signing_kp.pubkey);
+ }
+
+ /* store the descriptor! */
+ retval = handle_response_fetch_hsdesc_v3(conn, args);
+ tt_int_op(retval, == , 0);
+
+ /* fetch the descriptor and make sure it's there */
+ {
+ hs_cache_client_descriptor_t *cached_desc = NULL;
+ cached_desc = lookup_v3_desc_as_client(signing_kp.pubkey.pubkey);
+ tt_assert(cached_desc);
+ tt_str_op(cached_desc->encoded_desc, OP_EQ, published_desc_str);
+ }
+
+ done:
+ tor_free(args);
+ hs_descriptor_free(published_desc);
+ tor_free(published_desc_str);
+ if (conn) {
+ tor_free(conn->hs_ident);
+ tor_free(conn);
+ }
+}
+
struct testcase_t hs_cache[] = {
/* Encoding tests. */
{ "directory", test_directory, TT_FORK,
@@ -441,6 +505,8 @@ struct testcase_t hs_cache[] = {
NULL, NULL },
{ "upload_and_download_hs_desc", test_upload_and_download_hs_desc, TT_FORK,
NULL, NULL },
+ { "client_cache", test_client_cache, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
1
0

[tor/master] prop224: Rename hs_client_note_connection_attempt_succeeded()
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit b2a820958e8389853757c3e5ad59114bd82623d4
Author: David Goulet <dgoulet(a)torproject.org>
Date: Wed Jun 28 14:20:28 2017 -0400
prop224: Rename hs_client_note_connection_attempt_succeeded()
This is a static function so don't polute the hs_client_ namespace.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_client.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 66068092f..8cf98a6b9 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -18,10 +18,10 @@
#include "hs_client.h"
#include "router.h"
-/** A prop224 v3 HS circuit successfully connected to the hidden
- * service. Update the stream state at <b>hs_conn_ident</b> appropriately. */
+/* A v3 HS circuit successfully connected to the hidden service. Update the
+ * stream state at <b>hs_conn_ident</b> appropriately. */
static void
-hs_client_attempt_succeeded(const hs_ident_edge_conn_t *hs_conn_ident)
+note_connection_attempt_succeeded(const hs_ident_edge_conn_t *hs_conn_ident)
{
(void) hs_conn_ident;
@@ -153,7 +153,7 @@ hs_client_note_connection_attempt_succeeded(const edge_connection_t *conn)
}
if (conn->hs_ident) { /* It's v3: pass it to the prop224 handler */
- hs_client_attempt_succeeded(conn->hs_ident);
+ note_connection_attempt_succeeded(conn->hs_ident);
return;
} else if (conn->rend_data) { /* It's v2: pass it to the legacy handler */
rend_client_note_connection_attempt_ended(conn->rend_data);
1
0

[tor/master] prop224: Connect to v3 services in connection_ap_handle_onion().
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 776e796d96a9f60bf41f62cbf7cc3e16c8452bdf
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Thu Jun 1 14:17:49 2017 +0300
prop224: Connect to v3 services in connection_ap_handle_onion().
Recognize and handle v3 addresses.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/connection_edge.c | 122 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 90 insertions(+), 32 deletions(-)
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index a0aa7c195..8ef3379c4 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -76,6 +76,8 @@
#include "dirserv.h"
#include "hibernate.h"
#include "hs_common.h"
+#include "hs_cache.h"
+#include "hs_client.h"
#include "hs_circuit.h"
#include "main.h"
#include "nodelist.h"
@@ -1392,10 +1394,13 @@ connection_ap_handshake_rewrite(entry_connection_t *conn,
}
}
+/** We just received a SOCKS request in <b>conn</b> to an onion address of type
+ * <b>addresstype</b>. Start connecting to the onion service. */
static int
connection_ap_handle_onion(entry_connection_t *conn,
socks_request_t *socks,
- origin_circuit_t *circ)
+ origin_circuit_t *circ,
+ hostname_type_t addresstype)
{
time_t now = approx_time();
connection_t *base_conn = ENTRY_TO_CONN(conn);
@@ -1432,38 +1437,81 @@ connection_ap_handle_onion(entry_connection_t *conn,
return -1;
}
- /* Look up if we have client authorization configured for this hidden
- * service. If we do, associate it with the rend_data. */
- rend_service_authorization_t *client_auth =
- rend_client_lookup_service_authorization(socks->address);
+ /* Interface: Regardless of HS version after the block below we should have
+ set onion_address, rend_cache_lookup_result, and descriptor_is_usable. */
+ const char *onion_address = NULL;
+ int rend_cache_lookup_result = -ENOENT;
+ int descriptor_is_usable = 0;
+
+ if (addresstype == ONION_V2_HOSTNAME) { /* it's a v2 hidden service */
+ rend_cache_entry_t *entry = NULL;
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(socks->address);
+
+ const uint8_t *cookie = NULL;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service request.");
+ auth_type = client_auth->auth_type;
+ cookie = client_auth->descriptor_cookie;
+ }
+
+ /* Fill in the rend_data field so we can start doing a connection to
+ * a hidden service. */
+ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
+ rend_data_client_create(socks->address, NULL, (char *) cookie,
+ auth_type);
+ if (rend_data == NULL) {
+ return -1;
+ }
+ onion_address = rend_data_get_address(rend_data);
+ log_info(LD_REND,"Got a hidden service request for ID '%s'",
+ safe_str_client(onion_address));
- const uint8_t *cookie = NULL;
- rend_auth_type_t auth_type = REND_NO_AUTH;
- if (client_auth) {
- log_info(LD_REND, "Using previously configured client authorization "
- "for hidden service request.");
- auth_type = client_auth->auth_type;
- cookie = client_auth->descriptor_cookie;
- }
+ rend_cache_lookup_result = rend_cache_lookup_entry(onion_address,-1,
+ &entry);
+ if (!rend_cache_lookup_result && entry) {
+ descriptor_is_usable = rend_client_any_intro_points_usable(entry);
+ }
+ } else { /* it's a v3 hidden service */
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ const hs_descriptor_t *cached_desc = NULL;
+ int retval;
+ /* Create HS conn identifier with HS pubkey */
+ hs_ident_edge_conn_t *hs_conn_ident =
+ tor_malloc_zero(sizeof(hs_ident_edge_conn_t));
+
+ retval = hs_parse_address(socks->address, &hs_conn_ident->identity_pk,
+ NULL, NULL);
+ if (retval < 0) {
+ log_warn(LD_GENERAL, "failed to parse hs address");
+ tor_free(hs_conn_ident);
+ return -1;
+ }
+ ENTRY_TO_EDGE_CONN(conn)->hs_ident = hs_conn_ident;
- /* Fill in the rend_data field so we can start doing a connection to
- * a hidden service. */
- rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
- rend_data_client_create(socks->address, NULL, (char *) cookie,
- auth_type);
- if (rend_data == NULL) {
- return -1;
+ onion_address = socks->address;
+
+ /* Check the v3 desc cache */
+ cached_desc = hs_cache_lookup_as_client(&hs_conn_ident->identity_pk);
+ if (cached_desc) {
+ rend_cache_lookup_result = 0;
+ descriptor_is_usable = hs_client_any_intro_points_usable(cached_desc);
+ log_info(LD_GENERAL, "Found %s descriptor in cache for %s. %s.",
+ (descriptor_is_usable) ? "usable" : "unusable",
+ safe_str_client(onion_address),
+ (descriptor_is_usable) ? "Not fetching." : "Refecting.");
+ } else {
+ rend_cache_lookup_result = -ENOENT;
+ }
}
- const char *onion_address = rend_data_get_address(rend_data);
- log_info(LD_REND,"Got a hidden service request for ID '%s'",
- safe_str_client(onion_address));
/* Lookup the given onion address. If invalid, stop right now.
* Otherwise, we might have it in the cache or not. */
unsigned int refetch_desc = 0;
- rend_cache_entry_t *entry = NULL;
- const int rend_cache_lookup_result =
- rend_cache_lookup_entry(onion_address, -1, &entry);
if (rend_cache_lookup_result < 0) {
switch (-rend_cache_lookup_result) {
case EINVAL:
@@ -1474,6 +1522,8 @@ connection_ap_handle_onion(entry_connection_t *conn,
return -1;
case ENOENT:
/* We didn't have this; we should look it up. */
+ log_info(LD_REND, "No descriptor found in our cache for %s. Fetching.",
+ safe_str_client(onion_address));
refetch_desc = 1;
break;
default:
@@ -1491,12 +1541,18 @@ connection_ap_handle_onion(entry_connection_t *conn,
/* Now we have a descriptor but is it usable or not? If not, refetch.
* Also, a fetch could have been requested if the onion address was not
* found in the cache previously. */
- if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
+ if (refetch_desc || !descriptor_is_usable) {
+ edge_connection_t *edge_conn = ENTRY_TO_EDGE_CONN(conn);
connection_ap_mark_as_non_pending_circuit(conn);
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
- log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(onion_address));
- rend_client_refetch_v2_renddesc(rend_data);
+ if (addresstype == ONION_V2_HOSTNAME) {
+ tor_assert(edge_conn->rend_data);
+ rend_client_refetch_v2_renddesc(edge_conn->rend_data);
+ } else {
+ tor_assert(addresstype == ONION_V3_HOSTNAME);
+ tor_assert(edge_conn->hs_ident);
+ hs_client_refetch_hsdesc(&edge_conn->hs_ident->identity_pk);
+ }
return 0;
}
@@ -1676,7 +1732,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
/* Now, we handle everything that isn't a .onion address. */
- if (addresstype != ONION_V2_HOSTNAME) {
+ if (addresstype != ONION_V2_HOSTNAME && addresstype != ONION_V3_HOSTNAME) {
/* Not a hidden-service request. It's either a hostname or an IP,
* possibly with a .exit that we stripped off. We're going to check
* if we're allowed to connect/resolve there, and then launch the
@@ -1954,8 +2010,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return 0;
} else {
/* If we get here, it's a request for a .onion address! */
+ tor_assert(addresstype == ONION_V2_HOSTNAME ||
+ addresstype == ONION_V3_HOSTNAME);
tor_assert(!automap);
- if (connection_ap_handle_onion(conn, socks, circ) < 0) {
+ if (connection_ap_handle_onion(conn, socks, circ, addresstype) < 0) {
return -1;
}
}
1
0

[tor/master] prop224: Fix hidserv request code to work for both v2 and v3.
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 15c9b7e891dea655476dc77a07cef8824402fb00
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Thu Jun 1 13:56:43 2017 +0300
prop224: Fix hidserv request code to work for both v2 and v3.
See documentation of `last_hid_serv_requests_` for how it works. strmaps are
cool!
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_common.c | 121 ++++++++++++++++++++++++++++++----------------------
src/or/hs_common.h | 11 ++++-
src/or/rendclient.c | 8 ++--
3 files changed, 82 insertions(+), 58 deletions(-)
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 48d5f78bb..dbd384833 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -1325,8 +1325,8 @@ hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
/** Return the period for which a hidden service directory cannot be queried
* for the same descriptor ID again, taking TestingTorNetwork into account. */
-static time_t
-hsdir_requery_period(const or_options_t *options)
+time_t
+hs_hsdir_requery_period(const or_options_t *options)
{
tor_assert(options);
@@ -1337,17 +1337,25 @@ hsdir_requery_period(const or_options_t *options)
}
}
-/** Contains the last request times to hidden service directories for
- * certain queries; each key is a string consisting of the
- * concatenation of a base32-encoded HS directory identity digest and
- * base32-encoded HS descriptor ID; each value is a pointer to a time_t
- * holding the time of the last request for that descriptor ID to that
- * HS directory. */
+/** Tracks requests for fetching hidden service descriptors. It's used by
+ * hidden service clients, to avoid querying HSDirs that have already failed
+ * giving back a descriptor. The same data structure is used to track both v2
+ * and v3 HS descriptor requests.
+ *
+ * The string map is a key/value store that contains the last request times to
+ * hidden service directories for certain queries. Specifically:
+ *
+ * key = base32(hsdir_identity) + base32(hs_identity)
+ * value = time_t of last request for that hs_identity to that HSDir
+ *
+ * where 'hsdir_identity' is the identity digest of the HSDir node, and
+ * 'hs_identity' is the descriptor ID of the HS in the v2 case, or the ed25519
+ * identity public key of the HS in the v3 case. */
static strmap_t *last_hid_serv_requests_ = NULL;
/** Returns last_hid_serv_requests_, initializing it to a new strmap if
* necessary. */
-static strmap_t *
+STATIC strmap_t *
get_last_hid_serv_requests(void)
{
if (!last_hid_serv_requests_)
@@ -1355,30 +1363,26 @@ get_last_hid_serv_requests(void)
return last_hid_serv_requests_;
}
-#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
- REND_DESC_ID_V2_LEN_BASE32)
-
/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
- * assign the current time <b>now</b> and return that. Otherwise, return the
- * most recent request time, or 0 if no such request has been sent before.
- */
-static time_t
-lookup_last_hid_serv_request(routerstatus_t *hs_dir,
- const char *desc_id_base32,
- time_t now, int set)
+ * for descriptor request key <b>req_key_str</b> which is the descriptor ID
+ * for a v2 service or the blinded key for v3. If <b>set</b> is non-zero,
+ * assign the current time <b>now</b> and return that. Otherwise, return the
+ * most recent request time, or 0 if no such request has been sent before. */
+time_t
+hs_lookup_last_hid_serv_request(routerstatus_t *hs_dir,
+ const char *req_key_str,
+ time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- char hsdir_desc_comb_id[LAST_HID_SERV_REQUEST_KEY_LEN + 1];
+ char *hsdir_desc_comb_id = NULL;
time_t *last_request_ptr;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
+
+ /* Create the key */
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
- hsdir_id_base32,
- desc_id_base32);
- /* XXX++?? tor_assert(strlen(hsdir_desc_comb_id) ==
- LAST_HID_SERV_REQUEST_KEY_LEN); */
+ tor_asprintf(&hsdir_desc_comb_id, "%s%s", hsdir_id_base32, req_key_str);
+
if (set) {
time_t *oldptr;
last_request_ptr = tor_malloc_zero(sizeof(time_t));
@@ -1386,20 +1390,23 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
oldptr = strmap_set(last_hid_serv_requests, hsdir_desc_comb_id,
last_request_ptr);
tor_free(oldptr);
- } else
- last_request_ptr = strmap_get_lc(last_hid_serv_requests,
- hsdir_desc_comb_id);
+ } else {
+ last_request_ptr = strmap_get(last_hid_serv_requests,
+ hsdir_desc_comb_id);
+ }
+
+ tor_free(hsdir_desc_comb_id);
return (last_request_ptr) ? *last_request_ptr : 0;
}
/** Clean the history of request times to hidden service directories, so that
* it does not contain requests older than REND_HID_SERV_DIR_REQUERY_PERIOD
* seconds any more. */
-static void
-directory_clean_last_hid_serv_requests(time_t now)
+void
+hs_clean_last_hid_serv_requests(time_t now)
{
strmap_iter_t *iter;
- time_t cutoff = now - hsdir_requery_period(get_options());
+ time_t cutoff = now - hs_hsdir_requery_period(get_options());
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
@@ -1417,33 +1424,43 @@ directory_clean_last_hid_serv_requests(time_t now)
}
}
-/** Remove all requests related to the descriptor ID <b>desc_id</b> from the
- * history of times of requests to hidden service directories.
- * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN.
+/** Remove all requests related to the descriptor request key string
+ * <b>req_key_str</b> from the history of times of requests to hidden service
+ * directories.
*
* This is called from rend_client_note_connection_attempt_ended(), which
* must be idempotent, so any future changes to this function must leave it
* idempotent too. */
void
-purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
+hs_purge_hid_serv_from_last_hid_serv_requests(const char *req_key_str)
{
strmap_iter_t *iter;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
- /* Key is stored with the base32 encoded desc_id. */
- base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
- DIGEST_LEN);
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
const char *key;
void *val;
strmap_iter_get(iter, &key, &val);
- /* XXX++?? tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
- if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
- REND_DESC_ID_V2_LEN_BASE32,
- desc_id_base32,
- REND_DESC_ID_V2_LEN_BASE32)) {
+
+ /* XXX: The use of REND_DESC_ID_V2_LEN_BASE32 is very wrong in terms of
+ * semantic, see #23305. */
+
+ /* Length check on the strings we are about to compare. The "key" contains
+ * both the base32 HSDir identity digest and the requested key at the
+ * directory. The "req_key_str" can either be a base32 descriptor ID or a
+ * base64 blinded key which should be the second part of "key". BUG on
+ * this check because both strings are internally controlled so this
+ * should never happen. */
+ if (BUG((strlen(req_key_str) + REND_DESC_ID_V2_LEN_BASE32) <
+ strlen(key))) {
+ iter = strmap_iter_next(last_hid_serv_requests, iter);
+ continue;
+ }
+
+ /* Check if the tracked request matches our request key */
+ if (tor_memeq(key + REND_DESC_ID_V2_LEN_BASE32, req_key_str,
+ strlen(req_key_str))) {
iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
tor_free(val);
} else {
@@ -1457,9 +1474,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
* accessed all of the HSDir relays responsible for the descriptor
* recently. */
void
-rend_client_purge_last_hid_serv_requests(void)
+hs_purge_last_hid_serv_requests(void)
{
- /* Don't create the table if it doesn't exist yet (and it may very
+ /* Don't create the table if it doesn't exist yet (and it may very
* well not exist if the user hasn't accessed any HSes)... */
strmap_t *old_last_hid_serv_requests = last_hid_serv_requests_;
/* ... and let get_last_hid_serv_requests re-create it for us if
@@ -1496,15 +1513,15 @@ pick_hsdir(const char *desc_id, const char *desc_id_base32)
hid_serv_get_responsible_directories(responsible_dirs, desc_id);
/* Clean request history first. */
- directory_clean_last_hid_serv_requests(now);
+ hs_clean_last_hid_serv_requests(now);
/* Only select those hidden service directories to which we did not send a
* request recently and for which we have a router descriptor here. */
SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
- time_t last = lookup_last_hid_serv_request(dir, desc_id_base32,
- 0, 0);
+ time_t last = hs_lookup_last_hid_serv_request(dir, desc_id_base32,
+ 0, 0);
const node_t *node = node_get_by_id(dir->identity_digest);
- if (last + hsdir_requery_period(options) >= now ||
+ if (last + hs_hsdir_requery_period(options) >= now ||
!node || !node_has_descriptor(node)) {
SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
continue;
@@ -1536,7 +1553,7 @@ pick_hsdir(const char *desc_id, const char *desc_id_base32)
} else {
/* Remember that we are requesting a descriptor from this hidden service
* directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
+ hs_lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
}
return hs_dir;
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index aa810c0db..4692a3a01 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -187,8 +187,6 @@ const char *rend_data_get_desc_id(const rend_data_t *rend_data,
const uint8_t *rend_data_get_pk_digest(const rend_data_t *rend_data,
size_t *len_out);
-void rend_client_purge_last_hid_serv_requests(void);
-void purge_hid_serv_from_last_hid_serv_requests(const char *desc_id);
routerstatus_t *pick_hsdir(const char *desc_id, const char *desc_id_base32);
void hs_get_subcredential(const ed25519_public_key_t *identity_pk,
@@ -224,6 +222,14 @@ void hs_get_responsible_hsdirs(const ed25519_public_key_t *blinded_pk,
uint64_t time_period_num, int is_next_period,
int is_client, smartlist_t *responsible_dirs);
+time_t hs_hsdir_requery_period(const or_options_t *options);
+time_t hs_lookup_last_hid_serv_request(routerstatus_t *hs_dir,
+ const char *desc_id_base32,
+ time_t now, int set);
+void hs_clean_last_hid_serv_requests(time_t now);
+void hs_purge_hid_serv_from_last_hid_serv_requests(const char *desc_id);
+void hs_purge_last_hid_serv_requests(void);
+
int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn);
void hs_inc_rdv_stream_counter(origin_circuit_t *circ);
@@ -242,6 +248,7 @@ STATIC void get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out);
#ifdef TOR_UNIT_TESTS
+STATIC strmap_t *get_last_hid_serv_requests(void);
STATIC uint64_t get_time_period_length(void);
STATIC uint8_t *get_first_cached_disaster_srv(void);
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 28e02a65e..a621d27f9 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -42,7 +42,7 @@ rend_client_purge_state(void)
rend_cache_purge();
rend_cache_failure_purge();
rend_client_cancel_descriptor_fetches();
- rend_client_purge_last_hid_serv_requests();
+ hs_purge_last_hid_serv_requests();
}
/** Called when we've established a circuit to an introduction point:
@@ -636,7 +636,7 @@ fetch_v2_desc_by_addr(rend_data_t *rend_query, smartlist_t *hsdirs)
sizeof(descriptor_id)) != 0) {
/* Not equal from what we currently have so purge the last hid serv
* request cache and update the descriptor ID with the new value. */
- purge_hid_serv_from_last_hid_serv_requests(
+ hs_purge_hid_serv_from_last_hid_serv_requests(
rend_data->descriptor_id[chosen_replica]);
memcpy(rend_data->descriptor_id[chosen_replica], descriptor_id,
sizeof(rend_data->descriptor_id[chosen_replica]));
@@ -1036,14 +1036,14 @@ rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
for (replica = 0; replica < ARRAY_LENGTH(rend_data_v2->descriptor_id);
replica++) {
const char *desc_id = rend_data_v2->descriptor_id[replica];
- purge_hid_serv_from_last_hid_serv_requests(desc_id);
+ hs_purge_hid_serv_from_last_hid_serv_requests(desc_id);
}
log_info(LD_REND, "Connection attempt for %s has ended; "
"cleaning up temporary state.",
safe_str_client(onion_address));
} else {
/* We only have an ID for a fetch. Probably used by HSFETCH. */
- purge_hid_serv_from_last_hid_serv_requests(rend_data_v2->desc_id_fetch);
+ hs_purge_hid_serv_from_last_hid_serv_requests(rend_data_v2->desc_id_fetch);
}
}
1
0

24 Aug '17
commit b13ee8e4ae59f85ce75800aa7dd90cfe58c04a5e
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue Jul 18 13:56:19 2017 -0400
hs: Move link specifier encoding to a function
This commit only moves code into a function. The client code will need a way
to take a bunch of descriptor link specifier object and encode them into link
specifiers objects.
Make this a public function so it can be used outside of hs_descriptor.c.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_descriptor.c | 102 ++++++++++++++++++++++++++++---------------------
src/or/hs_descriptor.h | 5 +++
2 files changed, 64 insertions(+), 43 deletions(-)
diff --git a/src/or/hs_descriptor.c b/src/or/hs_descriptor.c
index 1e595f3a3..71f16fee1 100644
--- a/src/or/hs_descriptor.c
+++ b/src/or/hs_descriptor.c
@@ -332,50 +332,10 @@ encode_link_specifiers(const smartlist_t *specs)
SMARTLIST_FOREACH_BEGIN(specs, const hs_desc_link_specifier_t *,
spec) {
- link_specifier_t *ls = link_specifier_new();
- link_specifier_set_ls_type(ls, spec->type);
-
- switch (spec->type) {
- case LS_IPV4:
- link_specifier_set_un_ipv4_addr(ls,
- tor_addr_to_ipv4h(&spec->u.ap.addr));
- link_specifier_set_un_ipv4_port(ls, spec->u.ap.port);
- /* Four bytes IPv4 and two bytes port. */
- link_specifier_set_ls_len(ls, sizeof(spec->u.ap.addr.addr.in_addr) +
- sizeof(spec->u.ap.port));
- break;
- case LS_IPV6:
- {
- size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
- const uint8_t *in6_addr = tor_addr_to_in6_addr8(&spec->u.ap.addr);
- uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
- memcpy(ipv6_array, in6_addr, addr_len);
- link_specifier_set_un_ipv6_port(ls, spec->u.ap.port);
- /* Sixteen bytes IPv6 and two bytes port. */
- link_specifier_set_ls_len(ls, addr_len + sizeof(spec->u.ap.port));
- break;
+ link_specifier_t *ls = hs_desc_encode_lspec(spec);
+ if (ls) {
+ link_specifier_list_add_spec(lslist, ls);
}
- case LS_LEGACY_ID:
- {
- size_t legacy_id_len = link_specifier_getlen_un_legacy_id(ls);
- uint8_t *legacy_id_array = link_specifier_getarray_un_legacy_id(ls);
- memcpy(legacy_id_array, spec->u.legacy_id, legacy_id_len);
- link_specifier_set_ls_len(ls, legacy_id_len);
- break;
- }
- case LS_ED25519_ID:
- {
- size_t ed25519_id_len = link_specifier_getlen_un_ed25519_id(ls);
- uint8_t *ed25519_id_array = link_specifier_getarray_un_ed25519_id(ls);
- memcpy(ed25519_id_array, spec->u.ed25519_id, ed25519_id_len);
- link_specifier_set_ls_len(ls, ed25519_id_len);
- break;
- }
- default:
- tor_assert(0);
- }
-
- link_specifier_list_add_spec(lslist, ls);
} SMARTLIST_FOREACH_END(spec);
{
@@ -2575,3 +2535,59 @@ hs_descriptor_clear_intro_points(hs_descriptor_t *desc)
}
}
+/* From a descriptor link specifier object spec, returned a newly allocated
+ * link specifier object that is the encoded representation of spec. Return
+ * NULL on error. */
+link_specifier_t *
+hs_desc_encode_lspec(const hs_desc_link_specifier_t *spec)
+{
+ tor_assert(spec);
+
+ link_specifier_t *ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, spec->type);
+
+ switch (spec->type) {
+ case LS_IPV4:
+ link_specifier_set_un_ipv4_addr(ls,
+ tor_addr_to_ipv4h(&spec->u.ap.addr));
+ link_specifier_set_un_ipv4_port(ls, spec->u.ap.port);
+ /* Four bytes IPv4 and two bytes port. */
+ link_specifier_set_ls_len(ls, sizeof(spec->u.ap.addr.addr.in_addr) +
+ sizeof(spec->u.ap.port));
+ break;
+ case LS_IPV6:
+ {
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&spec->u.ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(ls, spec->u.ap.port);
+ /* Sixteen bytes IPv6 and two bytes port. */
+ link_specifier_set_ls_len(ls, addr_len + sizeof(spec->u.ap.port));
+ break;
+ }
+ case LS_LEGACY_ID:
+ {
+ size_t legacy_id_len = link_specifier_getlen_un_legacy_id(ls);
+ uint8_t *legacy_id_array = link_specifier_getarray_un_legacy_id(ls);
+ memcpy(legacy_id_array, spec->u.legacy_id, legacy_id_len);
+ link_specifier_set_ls_len(ls, legacy_id_len);
+ break;
+ }
+ case LS_ED25519_ID:
+ {
+ size_t ed25519_id_len = link_specifier_getlen_un_ed25519_id(ls);
+ uint8_t *ed25519_id_array = link_specifier_getarray_un_ed25519_id(ls);
+ memcpy(ed25519_id_array, spec->u.ed25519_id, ed25519_id_len);
+ link_specifier_set_ls_len(ls, ed25519_id_len);
+ break;
+ }
+ default:
+ tor_assert_nonfatal_unreached();
+ link_specifier_free(ls);
+ ls = NULL;
+ }
+
+ return ls;
+}
+
diff --git a/src/or/hs_descriptor.h b/src/or/hs_descriptor.h
index 256a04dd6..b4cda7b84 100644
--- a/src/or/hs_descriptor.h
+++ b/src/or/hs_descriptor.h
@@ -18,6 +18,9 @@
#include "crypto_ed25519.h"
#include "torcert.h"
+/* Trunnel */
+#include "ed25519_cert.h"
+
/* The earliest descriptor format version we support. */
#define HS_DESC_SUPPORTED_FORMAT_VERSION_MIN 3
/* The latest descriptor format version we support. */
@@ -230,6 +233,8 @@ size_t hs_desc_plaintext_obj_size(const hs_desc_plaintext_data_t *data);
hs_desc_intro_point_t *hs_desc_intro_point_new(void);
void hs_desc_intro_point_free(hs_desc_intro_point_t *ip);
+link_specifier_t *hs_desc_encode_lspec(const hs_desc_link_specifier_t *spec);
+
#ifdef HS_DESCRIPTOR_PRIVATE
/* Encoding. */
1
0

[tor/master] conn: Add a function to return a list of connection by state
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 6222eae8cabd7ab99e634ad7463a8c38d414fa19
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue Jul 18 11:41:41 2017 -0400
conn: Add a function to return a list of connection by state
This will be useful to the hidden service subsystem that needs to go over all
connections of a certain state to attach them to a hidden service circuit.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/connection.c | 21 +++++++++++++++++++++
src/or/connection.h | 1 +
2 files changed, 22 insertions(+)
diff --git a/src/or/connection.c b/src/or/connection.c
index 5c65e886c..31a682387 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -4102,6 +4102,27 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
}
}
+#define CONN_GET_ALL_TEMPLATE(var, test) \
+ STMT_BEGIN \
+ smartlist_t *conns = get_connection_array(); \
+ smartlist_t *ret_conns = smartlist_new(); \
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, var) { \
+ if (var && (test) && !var->marked_for_close) \
+ smartlist_add(ret_conns, var); \
+ } SMARTLIST_FOREACH_END(var); \
+ return ret_conns; \
+ STMT_END
+
+/* Return a list of connections that aren't close and matches the given state.
+ * The returned list can be empty and must be freed using smartlist_free().
+ * The caller does NOT have owernship of the objects in the list so it must
+ * not free them nor reference them as they can disapear. */
+smartlist_t *
+connection_list_by_type_state(int type, int state)
+{
+ CONN_GET_ALL_TEMPLATE(conn, (conn->type == type && conn->state == state));
+}
+
/** Return a connection_t * from get_connection_array() that satisfies test on
* var, and that is not marked for close. */
#define CONN_GET_TEMPLATE(var, test) \
diff --git a/src/or/connection.h b/src/or/connection.h
index 36e45aef3..0bcf0ccdc 100644
--- a/src/or/connection.h
+++ b/src/or/connection.h
@@ -182,6 +182,7 @@ MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
+smartlist_t *connection_list_by_type_state(int type, int state);
smartlist_t *connection_dir_list_by_purpose_and_resource(
int purpose,
const char *resource);
1
0

[tor/master] prop224: Helper function to assert on invalid client intro circuit
by nickm@torproject.org 24 Aug '17
by nickm@torproject.org 24 Aug '17
24 Aug '17
commit 8e2854372d777d6be63d1bf766ca6db9100490de
Author: David Goulet <dgoulet(a)torproject.org>
Date: Thu Jun 29 13:29:23 2017 -0400
prop224: Helper function to assert on invalid client intro circuit
Put all the possible assert() we can do on a client introduction circuit in
one helper function to make sure it is valid and usable.
It is disabled for now so gcc doesn't complain that we have a unused function.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_client.c | 14 ++++++++++++++
src/or/hs_common.c | 1 +
src/or/hs_ident.c | 22 ++++++++++++++++++++++
src/or/hs_ident.h | 3 +++
src/or/rendcommon.c | 2 +-
src/or/rendcommon.h | 2 +-
6 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 8cf98a6b9..514ecf99b 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -140,6 +140,20 @@ fetch_v3_desc(const ed25519_public_key_t *onion_identity_pk)
return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs);
}
+#if 0
+/* Make sure that the given origin circuit circ is a valid correct
+ * introduction circuit. This asserts on validation failure. */
+static void
+assert_intro_circ(const origin_circuit_t *circ)
+{
+ tor_assert(circ);
+ tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+ tor_assert(circ->hs_ident);
+ tor_assert(hs_ident_intro_circ_is_valid(circ->hs_ident));
+ assert_circ_anonymity_ok(circ, get_options());
+}
+#endif
+
/** A circuit just finished connecting to a hidden service that the stream
* <b>conn</b> has been waiting for. Let the HS subsystem know about this. */
void
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index bc44265d5..e0c7dca4b 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -18,6 +18,7 @@
#include "nodelist.h"
#include "hs_cache.h"
#include "hs_common.h"
+#include "hs_ident.h"
#include "hs_service.h"
#include "rendcommon.h"
#include "rendservice.h"
diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c
index e69350d82..df3928515 100644
--- a/src/or/hs_ident.c
+++ b/src/or/hs_ident.c
@@ -86,3 +86,25 @@ hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident)
tor_free(ident);
}
+/* Return true if the given ident is valid for an introduction circuit. */
+int
+hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident)
+{
+ if (ident == NULL) {
+ goto invalid;
+ }
+
+ if (ed25519_public_key_is_zero(&ident->identity_pk)) {
+ goto invalid;
+ }
+
+ if (ed25519_public_key_is_zero(&ident->intro_auth_pk)) {
+ goto invalid;
+ }
+
+ /* Valid. */
+ return 1;
+ invalid:
+ return 0;
+}
+
diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h
index e259fde54..cfcde781d 100644
--- a/src/or/hs_ident.h
+++ b/src/or/hs_ident.h
@@ -126,5 +126,8 @@ hs_ident_edge_conn_t *hs_ident_edge_conn_new(
const ed25519_public_key_t *identity_pk);
void hs_ident_edge_conn_free(hs_ident_edge_conn_t *ident);
+/* Validators */
+int hs_ident_intro_circ_is_valid(const hs_ident_circuit_t *ident);
+
#endif /* TOR_HS_IDENT_H */
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 8829ede96..a6b59881a 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -990,7 +990,7 @@ rend_non_anonymous_mode_enabled(const or_options_t *options)
* service.
*/
void
-assert_circ_anonymity_ok(origin_circuit_t *circ,
+assert_circ_anonymity_ok(const origin_circuit_t *circ,
const or_options_t *options)
{
tor_assert(options);
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index f03a57f2e..af8dd6009 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -60,7 +60,7 @@ int rend_auth_decode_cookie(const char *cookie_in,
int rend_allow_non_anonymous_connection(const or_options_t* options);
int rend_non_anonymous_mode_enabled(const or_options_t *options);
-void assert_circ_anonymity_ok(origin_circuit_t *circ,
+void assert_circ_anonymity_ok(const origin_circuit_t *circ,
const or_options_t *options);
#ifdef RENDCOMMON_PRIVATE
1
0

24 Aug '17
commit 8a552bf49bb94dbca6163e7063d776f5f3a96694
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue Jul 18 15:17:37 2017 -0400
prop224: Make lspecs to extend info public
The hs circuit file had this function that takes a list of link specifiers and
return a newly allocated extend info object. Make it public so the client side
can also use it to be able to extend to introduction point.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_circuit.c | 124 ++--------------------------------------------------
src/or/hs_common.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/or/hs_common.h | 4 ++
3 files changed, 129 insertions(+), 121 deletions(-)
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 6d1cdd4eb..7704fd0ba 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -341,125 +341,6 @@ send_establish_intro(const hs_service_t *service,
memwipe(payload, 0, sizeof(payload));
}
-/* From a list of link specifier, an onion key and if we are requesting a
- * direct connection (ex: single onion service), return a newly allocated
- * extend_info_t object. This function checks the firewall policies and if we
- * are allowed to extend to the chosen address.
- *
- * if either IPv4 or legacy ID is missing, error.
- * if not direct_conn, IPv4 is prefered.
- * if direct_conn, IPv6 is prefered if we have one available.
- * if firewall does not allow the chosen address, error.
- *
- * Return NULL if we can't fulfill the conditions. */
-static extend_info_t *
-get_rp_extend_info(const smartlist_t *link_specifiers,
- const curve25519_public_key_t *onion_key, int direct_conn)
-{
- int have_v4 = 0, have_v6 = 0, have_legacy_id = 0, have_ed25519_id = 0;
- char legacy_id[DIGEST_LEN] = {0};
- uint16_t port_v4 = 0, port_v6 = 0, port = 0;
- tor_addr_t addr_v4, addr_v6, *addr = NULL;
- ed25519_public_key_t ed25519_pk;
- extend_info_t *info = NULL;
-
- tor_assert(link_specifiers);
- tor_assert(onion_key);
-
- SMARTLIST_FOREACH_BEGIN(link_specifiers, const link_specifier_t *, ls) {
- switch (link_specifier_get_ls_type(ls)) {
- case LS_IPV4:
- /* Skip if we already seen a v4. */
- if (have_v4) continue;
- tor_addr_from_ipv4h(&addr_v4,
- link_specifier_get_un_ipv4_addr(ls));
- port_v4 = link_specifier_get_un_ipv4_port(ls);
- have_v4 = 1;
- break;
- case LS_IPV6:
- /* Skip if we already seen a v6. */
- if (have_v6) continue;
- tor_addr_from_ipv6_bytes(&addr_v6,
- (const char *) link_specifier_getconstarray_un_ipv6_addr(ls));
- port_v6 = link_specifier_get_un_ipv6_port(ls);
- have_v6 = 1;
- break;
- case LS_LEGACY_ID:
- /* Make sure we do have enough bytes for the legacy ID. */
- if (link_specifier_getlen_un_legacy_id(ls) < sizeof(legacy_id)) {
- break;
- }
- memcpy(legacy_id, link_specifier_getconstarray_un_legacy_id(ls),
- sizeof(legacy_id));
- have_legacy_id = 1;
- break;
- case LS_ED25519_ID:
- memcpy(ed25519_pk.pubkey,
- link_specifier_getconstarray_un_ed25519_id(ls),
- ED25519_PUBKEY_LEN);
- have_ed25519_id = 1;
- break;
- default:
- /* Ignore unknown. */
- break;
- }
- } SMARTLIST_FOREACH_END(ls);
-
- /* IPv4, legacy ID are mandatory for rend points.
- * ed25519 keys and ipv6 are optional for rend points */
- if (!have_v4 || !have_legacy_id) {
- goto done;
- }
- /* By default, we pick IPv4 but this might change to v6 if certain
- * conditions are met. */
- addr = &addr_v4; port = port_v4;
-
- /* If we are NOT in a direct connection, we'll use our Guard and a 3-hop
- * circuit so we can't extend in IPv6. And at this point, we do have an IPv4
- * address available so go to validation. */
- if (!direct_conn) {
- goto validate;
- }
-
- /* From this point on, we have a request for a direct connection to the
- * rendezvous point so make sure we can actually connect through our
- * firewall. We'll prefer IPv6. */
-
- /* IPv6 test. */
- if (have_v6 &&
- fascist_firewall_allows_address_addr(&addr_v6, port_v6,
- FIREWALL_OR_CONNECTION, 1, 1)) {
- /* Direct connection and we can reach it in IPv6 so go for it. */
- addr = &addr_v6; port = port_v6;
- goto validate;
- }
- /* IPv4 test and we are sure we have a v4 because of the check above. */
- if (fascist_firewall_allows_address_addr(&addr_v4, port_v4,
- FIREWALL_OR_CONNECTION, 0, 0)) {
- /* Direct connection and we can reach it in IPv4 so go for it. */
- addr = &addr_v4; port = port_v4;
- goto validate;
- }
-
- validate:
- /* We'll validate now that the address we've picked isn't a private one. If
- * it is, are we allowing to extend to private address? */
- if (!extend_info_addr_is_allowed(addr)) {
- log_warn(LD_REND, "Rendezvous point address is private and it is not "
- "allowed to extend to it: %s:%u",
- fmt_addr(&addr_v4), port_v4);
- goto done;
- }
-
- /* We do have everything for which we think we can connect successfully. */
- info = extend_info_new(NULL, legacy_id,
- have_ed25519_id ? &ed25519_pk : NULL,
- NULL, onion_key,
- addr, port);
- done:
- return info;
-}
-
/* For a given service, the ntor onion key and a rendezvous cookie, launch a
* circuit to the rendezvous point specified by the link specifiers. On
* success, a circuit identifier is attached to the circuit with the needed
@@ -483,8 +364,9 @@ launch_rendezvous_point_circuit(const hs_service_t *service,
/* Get the extend info data structure for the chosen rendezvous point
* specified by the given link specifiers. */
- info = get_rp_extend_info(data->link_specifiers, &data->onion_pk,
- service->config.is_single_onion);
+ info = hs_get_extend_info_from_lspecs(data->link_specifiers,
+ &data->onion_pk,
+ service->config.is_single_onion);
if (info == NULL) {
/* We are done here, we can't extend to the rendezvous point. */
goto end;
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index e0c7dca4b..03dd07f6c 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -14,12 +14,14 @@
#include "or.h"
#include "config.h"
+#include "circuitbuild.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "hs_cache.h"
#include "hs_common.h"
#include "hs_ident.h"
#include "hs_service.h"
+#include "policies.h"
#include "rendcommon.h"
#include "rendservice.h"
#include "routerset.h"
@@ -28,6 +30,9 @@
#include "shared_random.h"
#include "shared_random_state.h"
+/* Trunnel */
+#include "ed25519_cert.h"
+
/* Ed25519 Basepoint value. Taken from section 5 of
* https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03 */
static const char *str_ed25519_basepoint =
@@ -1559,6 +1564,123 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str)
return hs_dir;
}
+/* From a list of link specifier, an onion key and if we are requesting a
+ * direct connection (ex: single onion service), return a newly allocated
+ * extend_info_t object. This function checks the firewall policies and if we
+ * are allowed to extend to the chosen address.
+ *
+ * if either IPv4 or legacy ID is missing, error.
+ * if not direct_conn, IPv4 is prefered.
+ * if direct_conn, IPv6 is prefered if we have one available.
+ * if firewall does not allow the chosen address, error.
+ *
+ * Return NULL if we can fulfill the conditions. */
+extend_info_t *
+hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
+ const curve25519_public_key_t *onion_key,
+ int direct_conn)
+{
+ int have_v4 = 0, have_v6 = 0, have_legacy_id = 0, have_ed25519_id = 0;
+ char legacy_id[DIGEST_LEN] = {0};
+ uint16_t port_v4 = 0, port_v6 = 0, port = 0;
+ tor_addr_t addr_v4, addr_v6, *addr = NULL;
+ ed25519_public_key_t ed25519_pk;
+ extend_info_t *info = NULL;
+
+ tor_assert(lspecs);
+
+ SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
+ switch (link_specifier_get_ls_type(ls)) {
+ case LS_IPV4:
+ /* Skip if we already seen a v4. */
+ if (have_v4) continue;
+ tor_addr_from_ipv4h(&addr_v4,
+ link_specifier_get_un_ipv4_addr(ls));
+ port_v4 = link_specifier_get_un_ipv4_port(ls);
+ have_v4 = 1;
+ break;
+ case LS_IPV6:
+ /* Skip if we already seen a v6. */
+ if (have_v6) continue;
+ tor_addr_from_ipv6_bytes(&addr_v6,
+ (const char *) link_specifier_getconstarray_un_ipv6_addr(ls));
+ port_v6 = link_specifier_get_un_ipv6_port(ls);
+ have_v6 = 1;
+ break;
+ case LS_LEGACY_ID:
+ /* Make sure we do have enough bytes for the legacy ID. */
+ if (link_specifier_getlen_un_legacy_id(ls) < sizeof(legacy_id)) {
+ break;
+ }
+ memcpy(legacy_id, link_specifier_getconstarray_un_legacy_id(ls),
+ sizeof(legacy_id));
+ have_legacy_id = 1;
+ break;
+ case LS_ED25519_ID:
+ memcpy(ed25519_pk.pubkey,
+ link_specifier_getconstarray_un_ed25519_id(ls),
+ ED25519_PUBKEY_LEN);
+ have_ed25519_id = 1;
+ break;
+ default:
+ /* Ignore unknown. */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ls);
+
+ /* IPv4 and legacy ID are mandatory. */
+ if (!have_v4 || !have_legacy_id) {
+ goto done;
+ }
+ /* By default, we pick IPv4 but this might change to v6 if certain
+ * conditions are met. */
+ addr = &addr_v4; port = port_v4;
+
+ /* If we are NOT in a direct connection, we'll use our Guard and a 3-hop
+ * circuit so we can't extend in IPv6. And at this point, we do have an IPv4
+ * address available so go to validation. */
+ if (!direct_conn) {
+ goto validate;
+ }
+
+ /* From this point on, we have a request for a direct connection to the
+ * rendezvous point so make sure we can actually connect through our
+ * firewall. We'll prefer IPv6. */
+
+ /* IPv6 test. */
+ if (have_v6 &&
+ fascist_firewall_allows_address_addr(&addr_v6, port_v6,
+ FIREWALL_OR_CONNECTION, 1, 1)) {
+ /* Direct connection and we can reach it in IPv6 so go for it. */
+ addr = &addr_v6; port = port_v6;
+ goto validate;
+ }
+ /* IPv4 test and we are sure we have a v4 because of the check above. */
+ if (fascist_firewall_allows_address_addr(&addr_v4, port_v4,
+ FIREWALL_OR_CONNECTION, 0, 0)) {
+ /* Direct connection and we can reach it in IPv4 so go for it. */
+ addr = &addr_v4; port = port_v4;
+ goto validate;
+ }
+
+ validate:
+ /* We'll validate now that the address we've picked isn't a private one. If
+ * it is, are we allowing to extend to private address? */
+ if (!extend_info_addr_is_allowed(addr)) {
+ log_warn(LD_REND, "Requested address is private and it is not "
+ "allowed to extend to it: %s:%u",
+ fmt_addr(&addr_v4), port_v4);
+ goto done;
+ }
+
+ /* We do have everything for which we think we can connect successfully. */
+ info = extend_info_new(NULL, legacy_id,
+ (have_ed25519_id) ? &ed25519_pk : NULL, NULL,
+ onion_key, addr, port);
+ done:
+ return info;
+}
+
/***********************************************************************/
/* Initialize the entire HS subsytem. This is called in tor_init() before any
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index 6ad68d0a8..79d92d915 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -237,6 +237,10 @@ int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn);
void hs_inc_rdv_stream_counter(origin_circuit_t *circ);
void hs_dec_rdv_stream_counter(origin_circuit_t *circ);
+extend_info_t *hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
+ const curve25519_public_key_t *onion_key,
+ int direct_conn);
+
#ifdef HS_COMMON_PRIVATE
STATIC void get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out);
1
0

24 Aug '17
commit d599325b5e1f92395742c3380112b4d2d7cbe9db
Author: David Goulet <dgoulet(a)torproject.org>
Date: Fri Jul 14 10:16:48 2017 -0400
prop224: Build INTRODUCE1 cell and send logic
Add a function in hs_cell.{c|h} for a client to build an INTRODUCE1 cell using
an object that contains all the needed keys to do so.
Add an entry point in hs_client.c that allows a tor client to send an
INTRODUCE1 cell on a given introduction circuit.
It includes the building of the cell, sending it and the setup of the
rendezvous circuit with the circuit identifier.
The entry point function is still unused at this commit.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/hs_cell.c | 265 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/or/hs_cell.h | 30 ++++++
src/or/hs_circuit.c | 147 +++++++++++++++++++++++++++++
src/or/hs_circuit.h | 4 +
src/or/hs_client.c | 180 ++++++++++++++++++++++++++++++++++-
src/or/hs_client.h | 3 +
6 files changed, 626 insertions(+), 3 deletions(-)
diff --git a/src/or/hs_cell.c b/src/or/hs_cell.c
index 7728b7705..889cf7749 100644
--- a/src/or/hs_cell.c
+++ b/src/or/hs_cell.c
@@ -10,6 +10,7 @@
#include "config.h"
#include "rendservice.h"
#include "replaycache.h"
+#include "util.h"
#include "hs_cell.h"
#include "hs_ntor.h"
@@ -245,6 +246,229 @@ parse_introduce2_cell(const hs_service_t *service,
return -1;
}
+/* Set the onion public key onion_pk in cell, the encrypted section of an
+ * INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_onion_key(trn_cell_introduce_encrypted_t *cell,
+ const uint8_t *onion_pk)
+{
+ tor_assert(cell);
+ tor_assert(onion_pk);
+ /* There is only one possible key type for a non legacy cell. */
+ trn_cell_introduce_encrypted_set_onion_key_type(cell,
+ HS_CELL_ONION_KEY_TYPE_NTOR);
+ trn_cell_introduce_encrypted_set_onion_key_len(cell, CURVE25519_PUBKEY_LEN);
+ trn_cell_introduce_encrypted_setlen_onion_key(cell, CURVE25519_PUBKEY_LEN);
+ memcpy(trn_cell_introduce_encrypted_getarray_onion_key(cell), onion_pk,
+ trn_cell_introduce_encrypted_getlen_onion_key(cell));
+}
+
+/* Set the link specifiers in lspecs in cell, the encrypted section of an
+ * INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_link_spec(trn_cell_introduce_encrypted_t *cell,
+ const smartlist_t *lspecs)
+{
+ tor_assert(cell);
+ tor_assert(lspecs);
+ tor_assert(smartlist_len(lspecs) > 0);
+ tor_assert(smartlist_len(lspecs) <= UINT8_MAX);
+
+ uint8_t lspecs_num = (uint8_t) smartlist_len(lspecs);
+ trn_cell_introduce_encrypted_set_nspec(cell, lspecs_num);
+ /* We aren't duplicating the link specifiers object here which means that
+ * the ownership goes to the trn_cell_introduce_encrypted_t cell and those
+ * object will be freed when the cell is. */
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, ls,
+ trn_cell_introduce_encrypted_add_nspecs(cell, ls));
+}
+
+/* Set padding in the enc_cell only if needed that is the total length of both
+ * sections are below the mininum required for an INTRODUCE1 cell. */
+static void
+introduce1_set_encrypted_padding(const trn_cell_introduce1_t *cell,
+ trn_cell_introduce_encrypted_t *enc_cell)
+{
+ tor_assert(cell);
+ tor_assert(enc_cell);
+ /* This is the length we expect to have once encoded of the whole cell. */
+ ssize_t full_len = trn_cell_introduce1_encoded_len(cell) +
+ trn_cell_introduce_encrypted_encoded_len(enc_cell);
+ tor_assert(full_len > 0);
+ if (full_len < HS_CELL_INTRODUCE1_MIN_SIZE) {
+ size_t padding = HS_CELL_INTRODUCE1_MIN_SIZE - full_len;
+ trn_cell_introduce_encrypted_setlen_pad(enc_cell, padding);
+ memset(trn_cell_introduce_encrypted_getarray_pad(enc_cell), 0,
+ trn_cell_introduce_encrypted_getlen_pad(enc_cell));
+ }
+}
+
+/* Encrypt the ENCRYPTED payload and encode it in the cell using the enc_cell
+ * and the INTRODUCE1 data.
+ *
+ * This can't fail but it is very important that the caller sets every field
+ * in data so the computation of the INTRODUCE1 keys doesn't fail. */
+static void
+introduce1_encrypt_and_encode(trn_cell_introduce1_t *cell,
+ const trn_cell_introduce_encrypted_t *enc_cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ size_t offset = 0;
+ ssize_t encrypted_len;
+ ssize_t encoded_cell_len, encoded_enc_cell_len;
+ uint8_t encoded_cell[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t encoded_enc_cell[RELAY_PAYLOAD_SIZE] = {0};
+ uint8_t *encrypted = NULL;
+ uint8_t mac[DIGEST256_LEN];
+ crypto_cipher_t *cipher = NULL;
+ hs_ntor_intro_cell_keys_t keys;
+
+ tor_assert(cell);
+ tor_assert(enc_cell);
+ tor_assert(data);
+
+ /* Encode the cells up to now of what we have to we can perform the MAC
+ * computation on it. */
+ encoded_cell_len = trn_cell_introduce1_encode(encoded_cell,
+ sizeof(encoded_cell), cell);
+ /* We have a much more serious issue if this isn't true. */
+ tor_assert(encoded_cell_len > 0);
+
+ encoded_enc_cell_len =
+ trn_cell_introduce_encrypted_encode(encoded_enc_cell,
+ sizeof(encoded_enc_cell), enc_cell);
+ /* We have a much more serious issue if this isn't true. */
+ tor_assert(encoded_enc_cell_len > 0);
+
+ /* Get the key material for the encryption. */
+ if (hs_ntor_client_get_introduce1_keys(data->auth_pk, data->enc_pk,
+ data->client_kp,
+ data->subcredential, &keys) < 0) {
+ tor_assert_unreached();
+ }
+
+ /* Prepare cipher with the encryption key just computed. */
+ cipher = crypto_cipher_new_with_bits((const char *) keys.enc_key,
+ sizeof(keys.enc_key) * 8);
+ tor_assert(cipher);
+
+ /* Compute the length of the ENCRYPTED section which is the CLIENT_PK,
+ * ENCRYPTED_DATA and MAC length. */
+ encrypted_len = sizeof(data->client_kp->pubkey) + encoded_enc_cell_len +
+ sizeof(mac);
+ tor_assert(encrypted_len < RELAY_PAYLOAD_SIZE);
+ encrypted = tor_malloc_zero(encrypted_len);
+
+ /* Put the CLIENT_PK first. */
+ memcpy(encrypted, data->client_kp->pubkey.public_key,
+ sizeof(data->client_kp->pubkey.public_key));
+ offset += sizeof(data->client_kp->pubkey.public_key);
+ /* Then encrypt and set the ENCRYPTED_DATA. This can't fail. */
+ crypto_cipher_encrypt(cipher, (char *) encrypted + offset,
+ (const char *) encoded_enc_cell, encoded_enc_cell_len);
+ crypto_cipher_free(cipher);
+ offset += encoded_enc_cell_len;
+ /* Compute MAC from the above and put it in the buffer. This function will
+ * make the adjustment to the encryptled_len to ommit the MAC length. */
+ compute_introduce_mac(encoded_cell, encoded_cell_len,
+ encrypted, encrypted_len,
+ keys.mac_key, sizeof(keys.mac_key),
+ mac, sizeof(mac));
+ memcpy(encrypted + offset, mac, sizeof(mac));
+ offset += sizeof(mac);
+ tor_assert(offset == (size_t) encrypted_len);
+
+ /* Set the ENCRYPTED section in the cell. */
+ trn_cell_introduce1_setlen_encrypted(cell, encrypted_len);
+ memcpy(trn_cell_introduce1_getarray_encrypted(cell),
+ encrypted, encrypted_len);
+
+ /* Cleanup. */
+ memwipe(&keys, 0, sizeof(keys));
+ memwipe(mac, 0, sizeof(mac));
+ memwipe(encrypted, 0, sizeof(encrypted_len));
+ memwipe(encoded_enc_cell, 0, sizeof(encoded_enc_cell));
+ tor_free(encrypted);
+}
+
+/* Using the INTRODUCE1 data, setup the ENCRYPTED section in cell. This means
+ * set it, encrypt it and encode it. */
+static void
+introduce1_set_encrypted(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ trn_cell_introduce_encrypted_t *enc_cell;
+ trn_cell_extension_t *ext;
+
+ tor_assert(cell);
+ tor_assert(data);
+
+ enc_cell = trn_cell_introduce_encrypted_new();
+ tor_assert(enc_cell);
+
+ /* Set extension data. None are used. */
+ ext = trn_cell_extension_new();
+ tor_assert(ext);
+ trn_cell_extension_set_num(ext, 0);
+ trn_cell_introduce_encrypted_set_extensions(enc_cell, ext);
+
+ /* Set the rendezvous cookie. */
+ memcpy(trn_cell_introduce_encrypted_getarray_rend_cookie(enc_cell),
+ data->rendezvous_cookie, REND_COOKIE_LEN);
+
+ /* Set the onion public key. */
+ introduce1_set_encrypted_onion_key(enc_cell, data->onion_pk->public_key);
+
+ /* Set the link specifiers. */
+ introduce1_set_encrypted_link_spec(enc_cell, data->link_specifiers);
+
+ /* Set padding. */
+ introduce1_set_encrypted_padding(cell, enc_cell);
+
+ /* Encrypt and encode it in the cell. */
+ introduce1_encrypt_and_encode(cell, enc_cell, data);
+
+ /* Cleanup. */
+ trn_cell_introduce_encrypted_free(enc_cell);
+}
+
+/* Set the authentication key in the INTRODUCE1 cell from the given data. */
+static void
+introduce1_set_auth_key(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ tor_assert(cell);
+ tor_assert(data);
+ /* There is only one possible type for a non legacy cell. */
+ trn_cell_introduce1_set_auth_key_type(cell, HS_INTRO_AUTH_KEY_TYPE_ED25519);
+ trn_cell_introduce1_set_auth_key_len(cell, ED25519_PUBKEY_LEN);
+ trn_cell_introduce1_setlen_auth_key(cell, ED25519_PUBKEY_LEN);
+ memcpy(trn_cell_introduce1_getarray_auth_key(cell),
+ data->auth_pk->pubkey, trn_cell_introduce1_getlen_auth_key(cell));
+}
+
+/* Set the legacy ID field in the INTRODUCE1 cell from the given data. */
+static void
+introduce1_set_legacy_id(trn_cell_introduce1_t *cell,
+ const hs_cell_introduce1_data_t *data)
+{
+ tor_assert(cell);
+ tor_assert(data);
+
+ if (data->is_legacy) {
+ uint8_t digest[DIGEST_LEN];
+ if (BUG(crypto_pk_get_digest(data->legacy_key, (char *) digest) < 0)) {
+ return;
+ }
+ memcpy(trn_cell_introduce1_getarray_legacy_key_id(cell),
+ digest, trn_cell_introduce1_getlen_legacy_key_id(cell));
+ } else {
+ /* We have to zeroed the LEGACY_KEY_ID field. */
+ memset(trn_cell_introduce1_getarray_legacy_key_id(cell), 0,
+ trn_cell_introduce1_getlen_legacy_key_id(cell));
+ }
+}
+
/* ========== */
/* Public API */
/* ========== */
@@ -582,3 +806,44 @@ hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
return cell_len;
}
+/* Build an INTRODUCE1 cell from the given data. The encoded cell is put in
+ * cell_out which must be of at least size RELAY_PAYLOAD_SIZE. On success, the
+ * encoded length is returned else a negative value and the content of
+ * cell_out should be ignored. */
+ssize_t
+hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
+ uint8_t *cell_out)
+{
+ ssize_t cell_len;
+ trn_cell_introduce1_t *cell;
+ trn_cell_extension_t *ext;
+
+ tor_assert(data);
+ tor_assert(cell_out);
+
+ cell = trn_cell_introduce1_new();
+ tor_assert(cell);
+
+ /* Set extension data. None are used. */
+ ext = trn_cell_extension_new();
+ tor_assert(ext);
+ trn_cell_extension_set_num(ext, 0);
+ trn_cell_introduce1_set_extensions(cell, ext);
+
+ /* Set the legacy ID field. */
+ introduce1_set_legacy_id(cell, data);
+
+ /* Set the authentication key. */
+ introduce1_set_auth_key(cell, data);
+
+ /* Set the encrypted section. This will set, encrypt and encode the
+ * ENCRYPTED section in the cell. After this, we'll be ready to encode. */
+ introduce1_set_encrypted(cell, data);
+
+ /* Final encoding. */
+ cell_len = trn_cell_introduce1_encode(cell_out, RELAY_PAYLOAD_SIZE, cell);
+
+ trn_cell_introduce1_free(cell);
+ return cell_len;
+}
+
diff --git a/src/or/hs_cell.h b/src/or/hs_cell.h
index f32f7a421..a72009510 100644
--- a/src/or/hs_cell.h
+++ b/src/or/hs_cell.h
@@ -12,11 +12,39 @@
#include "or.h"
#include "hs_service.h"
+/* An INTRODUCE1 cell requires at least this amount of bytes (see section
+ * 3.2.2 of the specification). Below this value, the cell must be padded. */
+#define HS_CELL_INTRODUCE1_MIN_SIZE 246
+
/* Onion key type found in the INTRODUCE1 cell. */
typedef enum {
HS_CELL_ONION_KEY_TYPE_NTOR = 1,
} hs_cell_onion_key_type_t;
+/* This data structure contains data that we need to build an INTRODUCE1 cell
+ * used by the INTRODUCE1 build function. */
+typedef struct hs_cell_introduce1_data_t {
+ /* Is this a legacy introduction point? */
+ unsigned int is_legacy : 1;
+ /* (Legacy only) The encryption key for a legacy intro point. Only set if
+ * is_legacy is true. */
+ const crypto_pk_t *legacy_key;
+ /* Introduction point authentication public key. */
+ const ed25519_public_key_t *auth_pk;
+ /* Introduction point encryption public key. */
+ const curve25519_public_key_t *enc_pk;
+ /* Subcredentials of the service. */
+ const uint8_t *subcredential;
+ /* Onion public key for the ntor handshake. */
+ const curve25519_public_key_t *onion_pk;
+ /* Rendezvous cookie. */
+ const uint8_t *rendezvous_cookie;
+ /* Public key put before the encrypted data (CLIENT_PK). */
+ const curve25519_keypair_t *client_kp;
+ /* Rendezvous point link specifiers. */
+ smartlist_t *link_specifiers;
+} hs_cell_introduce1_data_t;
+
/* This data structure contains data that we need to parse an INTRODUCE2 cell
* which is used by the INTRODUCE2 cell parsing function. On a successful
* parsing, the onion_pk and rendezvous_cookie will be populated with the
@@ -63,6 +91,8 @@ ssize_t hs_cell_build_rendezvous1(const uint8_t *rendezvous_cookie,
const uint8_t *rendezvous_handshake_info,
size_t rendezvous_handshake_info_len,
uint8_t *cell_out);
+ssize_t hs_cell_build_introduce1(const hs_cell_introduce1_data_t *data,
+ uint8_t *cell_out);
/* Parse cell API. */
ssize_t hs_cell_parse_intro_established(const uint8_t *payload,
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c
index 7704fd0ba..95100e9b3 100644
--- a/src/or/hs_circuit.c
+++ b/src/or/hs_circuit.c
@@ -530,6 +530,83 @@ retry_service_rendezvous_point(const origin_circuit_t *circ)
return;
}
+/* Using an extend info object ei, set all possible link specifiers in lspecs.
+ * IPv4, legacy ID and ed25519 ID are mandatory thus MUST be present in ei. */
+static void
+get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs)
+{
+ link_specifier_t *ls;
+
+ tor_assert(ei);
+ tor_assert(lspecs);
+
+ /* IPv4 is mandatory. */
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr));
+ link_specifier_set_un_ipv4_port(ls, ei->port);
+ /* Four bytes IPv4 and two bytes port. */
+ link_specifier_set_ls_len(ls, sizeof(ei->addr.addr.in_addr) +
+ sizeof(ei->port));
+ smartlist_add(lspecs, ls);
+
+ /* Legacy ID is mandatory. */
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_LEGACY_ID);
+ memcpy(link_specifier_getarray_un_legacy_id(ls), ei->identity_digest,
+ link_specifier_getlen_un_legacy_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls));
+ smartlist_add(lspecs, ls);
+
+ /* ed25519 ID is mandatory. */
+ ls = link_specifier_new();
+ link_specifier_set_ls_type(ls, LS_ED25519_ID);
+ memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity,
+ link_specifier_getlen_un_ed25519_id(ls));
+ link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls));
+ smartlist_add(lspecs, ls);
+
+ /* XXX: IPv6 is not clearly a thing in extend_info_t? */
+}
+
+/* Using the given descriptor intro point ip, the extend information of the
+ * rendezvous point rp_ei and the service's subcredential, populate the
+ * already allocated intro1_data object with the needed key material and link
+ * specifiers.
+ *
+ * This can't fail but the ip MUST be a valid object containing the needed
+ * keys and authentication method. */
+static void
+setup_introduce1_data(const hs_desc_intro_point_t *ip,
+ const extend_info_t *rp_ei,
+ const uint8_t *subcredential,
+ hs_cell_introduce1_data_t *intro1_data)
+{
+ smartlist_t *rp_lspecs;
+
+ tor_assert(ip);
+ tor_assert(rp_ei);
+ tor_assert(subcredential);
+ tor_assert(intro1_data);
+
+ /* Build the link specifiers from the extend information of the rendezvous
+ * circuit that we've picked previously. */
+ rp_lspecs = smartlist_new();
+ get_lspecs_from_extend_info(rp_ei, rp_lspecs);
+
+ /* Populate the introduce1 data object. */
+ memset(intro1_data, 0, sizeof(hs_cell_introduce1_data_t));
+ if (ip->legacy.key != NULL) {
+ intro1_data->is_legacy = 1;
+ intro1_data->legacy_key = ip->legacy.key;
+ }
+ intro1_data->auth_pk = &ip->auth_key_cert->signed_key;
+ intro1_data->enc_pk = &ip->enc_key;
+ intro1_data->subcredential = subcredential;
+ intro1_data->onion_pk = &rp_ei->curve25519_onion_key;
+ intro1_data->link_specifiers = rp_lspecs;
+}
+
/* ========== */
/* Public API */
/* ========== */
@@ -937,3 +1014,73 @@ hs_circuit_setup_e2e_rend_circ_legacy_client(origin_circuit_t *circ,
return 0;
}
+/* Given the introduction circuit intro_circ, the rendezvous circuit
+ * rend_circ, a descriptor intro point object ip and the service's
+ * subcredential, send an INTRODUCE1 cell on intro_circ.
+ *
+ * This will also setup the circuit identifier on rend_circ containing the key
+ * material for the handshake and e2e encryption. Return 0 on success else
+ * negative value. Because relay_send_command_from_edge() closes the circuit
+ * on error, it is possible that intro_circ is closed on error. */
+int
+hs_circ_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ,
+ const hs_desc_intro_point_t *ip,
+ const uint8_t *subcredential)
+{
+ int ret = -1;
+ ssize_t payload_len;
+ uint8_t payload[RELAY_PAYLOAD_SIZE] = {0};
+ hs_cell_introduce1_data_t intro1_data;
+
+ tor_assert(intro_circ);
+ tor_assert(rend_circ);
+ tor_assert(ip);
+ tor_assert(subcredential);
+
+ /* This takes various objects in order to populate the introduce1 data
+ * object which is used to build the content of the cell. */
+ setup_introduce1_data(ip, rend_circ->build_state->chosen_exit,
+ subcredential, &intro1_data);
+
+ /* Final step before we encode a cell, we setup the circuit identifier which
+ * will generate both the rendezvous cookie and client keypair for this
+ * connection. Those are put in the ident. */
+ intro1_data.rendezvous_cookie = rend_circ->hs_ident->rendezvous_cookie;
+ intro1_data.client_kp = &rend_circ->hs_ident->rendezvous_client_kp;
+
+ memcpy(intro_circ->hs_ident->rendezvous_cookie,
+ rend_circ->hs_ident->rendezvous_cookie,
+ sizeof(intro_circ->hs_ident->rendezvous_cookie));
+
+ /* From the introduce1 data object, this will encode the INTRODUCE1 cell
+ * into payload which is then ready to be sent as is. */
+ payload_len = hs_cell_build_introduce1(&intro1_data, payload);
+ if (BUG(payload_len < 0)) {
+ goto done;
+ }
+
+ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(intro_circ),
+ RELAY_COMMAND_INTRODUCE1,
+ (const char *) payload, payload_len,
+ intro_circ->cpath->prev) < 0) {
+ /* On error, circuit is closed. */
+ log_warn(LD_REND, "Unable to send INTRODUCE1 cell on circuit %u.",
+ TO_CIRCUIT(intro_circ)->n_circ_id);
+ goto done;
+ }
+
+ /* Success. */
+ ret = 0;
+ goto done;
+
+ done:
+ /* Object in this list have been moved to the cell object when building it
+ * so they've been freed earlier. We do that in order to avoid duplicating
+ * them leading to more memory and CPU time being used for nothing. */
+ smartlist_free(intro1_data.link_specifiers);
+ memwipe(&intro1_data, 0, sizeof(intro1_data));
+ memwipe(payload, 0, sizeof(payload));
+ return ret;
+}
+
diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h
index 9e359394e..f35ebf17d 100644
--- a/src/or/hs_circuit.h
+++ b/src/or/hs_circuit.h
@@ -44,6 +44,10 @@ int hs_circ_handle_introduce2(const hs_service_t *service,
hs_service_intro_point_t *ip,
const uint8_t *subcredential,
const uint8_t *payload, size_t payload_len);
+int hs_circ_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ,
+ const hs_desc_intro_point_t *ip,
+ const uint8_t *subcredential);
/* e2e circuit API. */
diff --git a/src/or/hs_client.c b/src/or/hs_client.c
index 514ecf99b..3f951a21a 100644
--- a/src/or/hs_client.c
+++ b/src/or/hs_client.c
@@ -10,13 +10,48 @@
#include "hs_circuit.h"
#include "hs_ident.h"
#include "connection_edge.h"
+#include "container.h"
#include "rendclient.h"
#include "hs_descriptor.h"
#include "hs_cache.h"
+#include "hs_cell.h"
+#include "hs_ident.h"
#include "config.h"
#include "directory.h"
#include "hs_client.h"
#include "router.h"
+#include "circuitlist.h"
+#include "circuituse.h"
+#include "connection.h"
+#include "circpathbias.h"
+
+/* Get all connections that are waiting on a circuit and flag them back to
+ * waiting for a hidden service descriptor for the given service key
+ * service_identity_pk. */
+static void
+flag_all_conn_wait_desc(const ed25519_public_key_t *service_identity_pk)
+{
+ tor_assert(service_identity_pk);
+
+ smartlist_t *conns =
+ connection_list_by_type_state(CONN_TYPE_AP, AP_CONN_STATE_CIRCUIT_WAIT);
+
+ SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
+ edge_connection_t *edge_conn;
+ if (BUG(!CONN_IS_EDGE(conn))) {
+ continue;
+ }
+ edge_conn = TO_EDGE_CONN(conn);
+ if (edge_conn->hs_ident &&
+ ed25519_pubkey_eq(&edge_conn->hs_ident->identity_pk,
+ service_identity_pk)) {
+ connection_ap_mark_as_non_pending_circuit(TO_ENTRY_CONN(conn));
+ conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+ }
+ } SMARTLIST_FOREACH_END(conn);
+
+ smartlist_free(conns);
+}
/* A v3 HS circuit successfully connected to the hidden service. Update the
* stream state at <b>hs_conn_ident</b> appropriately. */
@@ -140,11 +175,10 @@ fetch_v3_desc(const ed25519_public_key_t *onion_identity_pk)
return directory_launch_v3_desc_fetch(onion_identity_pk, hsdir_rs);
}
-#if 0
/* Make sure that the given origin circuit circ is a valid correct
* introduction circuit. This asserts on validation failure. */
static void
-assert_intro_circ(const origin_circuit_t *circ)
+assert_intro_circ_ok(const origin_circuit_t *circ)
{
tor_assert(circ);
tor_assert(circ->base_.purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
@@ -152,7 +186,131 @@ assert_intro_circ(const origin_circuit_t *circ)
tor_assert(hs_ident_intro_circ_is_valid(circ->hs_ident));
assert_circ_anonymity_ok(circ, get_options());
}
-#endif
+
+/* Find a descriptor intro point object that matches the given ident in the
+ * given descriptor desc. Return NULL if not found. */
+static const hs_desc_intro_point_t *
+find_desc_intro_point_by_ident(const hs_ident_circuit_t *ident,
+ const hs_descriptor_t *desc)
+{
+ const hs_desc_intro_point_t *intro_point = NULL;
+
+ tor_assert(ident);
+ tor_assert(desc);
+
+ SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
+ const hs_desc_intro_point_t *, ip) {
+ if (ed25519_pubkey_eq(&ident->intro_auth_pk,
+ &ip->auth_key_cert->signed_key)) {
+ intro_point = ip;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ip);
+
+ return intro_point;
+}
+
+/* Send an INTRODUCE1 cell along the intro circuit and populate the rend
+ * circuit identifier with the needed key material for the e2e encryption.
+ * Return 0 on success, -1 if there is a transient error such that an action
+ * has been taken to recover and -2 if there is a permanent error indicating
+ * that both circuits were closed. */
+static int
+send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ)
+{
+ int status;
+ char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
+ const ed25519_public_key_t *service_identity_pk = NULL;
+ const hs_desc_intro_point_t *ip;
+
+ assert_intro_circ_ok(intro_circ);
+ tor_assert(rend_circ);
+
+ service_identity_pk = &intro_circ->hs_ident->identity_pk;
+ /* For logging purposes. There will be a time where the hs_ident will have a
+ * version number but for now there is none because it's all v3. */
+ hs_build_address(service_identity_pk, HS_VERSION_THREE, onion_address);
+
+ log_info(LD_REND, "Sending INTRODUCE1 cell to service %s on circuit %u",
+ safe_str_client(onion_address), TO_CIRCUIT(intro_circ)->n_circ_id);
+
+ /* 1) Get descriptor from our cache. */
+ const hs_descriptor_t *desc =
+ hs_cache_lookup_as_client(service_identity_pk);
+ if (desc == NULL || !hs_client_any_intro_points_usable(desc)) {
+ log_info(LD_REND, "Request to %s %s. Trying to fetch a new descriptor.",
+ safe_str_client(onion_address),
+ (desc) ? "didn't have usable intro points" :
+ "didn't have a descriptor");
+ hs_client_refetch_hsdesc(service_identity_pk);
+ /* We just triggered a refetch, make sure every connections are back
+ * waiting for that descriptor. */
+ flag_all_conn_wait_desc(service_identity_pk);
+ /* We just asked for a refetch so this is a transient error. */
+ goto tran_err;
+ }
+
+ /* We need to find which intro point in the descriptor we are connected to
+ * on intro_circ. */
+ ip = find_desc_intro_point_by_ident(intro_circ->hs_ident, desc);
+ if (BUG(ip == NULL)) {
+ /* If we can find a descriptor from this introduction circuit ident, we
+ * must have a valid intro point object. Permanent error. */
+ goto perm_err;
+ }
+
+ /* Send the INTRODUCE1 cell. */
+ if (hs_circ_send_introduce1(intro_circ, rend_circ, ip,
+ desc->subcredential) < 0) {
+ /* Unable to send the cell, the intro circuit has been marked for close so
+ * this is a permanent error. */
+ tor_assert_nonfatal(TO_CIRCUIT(intro_circ)->marked_for_close);
+ goto perm_err;
+ }
+
+ /* Cell has been sent successfully. Copy the introduction point
+ * authentication and encryption key in the rendezvous circuit identifier so
+ * we can compute the ntor keys when we receive the RENDEZVOUS2 cell. */
+ memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key,
+ sizeof(rend_circ->hs_ident->intro_enc_pk));
+ ed25519_pubkey_copy(&rend_circ->hs_ident->intro_auth_pk,
+ &intro_circ->hs_ident->intro_auth_pk);
+
+ /* Now, we wait for an ACK or NAK on this circuit. */
+ circuit_change_purpose(TO_CIRCUIT(intro_circ),
+ CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT);
+ /* Set timestamp_dirty, because circuit_expire_building expects it to
+ * specify when a circuit entered the _C_INTRODUCE_ACK_WAIT state. */
+ TO_CIRCUIT(intro_circ)->timestamp_dirty = time(NULL);
+ pathbias_count_use_attempt(intro_circ);
+
+ /* Success. */
+ status = 0;
+ goto end;
+
+ perm_err:
+ /* Permanent error: it is possible that the intro circuit was closed prior
+ * because we weren't able to send the cell. Make sure we don't double close
+ * it which would result in a warning. */
+ if (!TO_CIRCUIT(intro_circ)->marked_for_close) {
+ circuit_mark_for_close(TO_CIRCUIT(intro_circ), END_CIRC_REASON_INTERNAL);
+ }
+ circuit_mark_for_close(TO_CIRCUIT(rend_circ), END_CIRC_REASON_INTERNAL);
+ status = -2;
+ goto end;
+
+ tran_err:
+ status = -1;
+
+ end:
+ memwipe(onion_address, 0, sizeof(onion_address));
+ return status;
+}
+
+/* ========== */
+/* Public API */
+/* ========== */
/** A circuit just finished connecting to a hidden service that the stream
* <b>conn</b> has been waiting for. Let the HS subsystem know about this. */
@@ -256,3 +414,19 @@ hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk)
return fetch_v3_desc(identity_pk);
}
+/* This is called when we are trying to attach an AP connection to these
+ * hidden service circuits from connection_ap_handshake_attach_circuit().
+ * Return 0 on success, -1 for a transient error that is actions were
+ * triggered to recover or -2 for a permenent error where both circuits will
+ * marked for close.
+ *
+ * The following supports every hidden service version. */
+int
+hs_client_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ)
+{
+ return (intro_circ->hs_ident) ? send_introduce1(intro_circ, rend_circ) :
+ rend_client_send_introduction(intro_circ,
+ rend_circ);
+}
+
diff --git a/src/or/hs_client.h b/src/or/hs_client.h
index 0b446c00b..d8348ddb3 100644
--- a/src/or/hs_client.h
+++ b/src/or/hs_client.h
@@ -22,5 +22,8 @@ int hs_client_decode_descriptor(
int hs_client_any_intro_points_usable(const hs_descriptor_t *desc);
int hs_client_refetch_hsdesc(const ed25519_public_key_t *identity_pk);
+int hs_client_send_introduce1(origin_circuit_t *intro_circ,
+ origin_circuit_t *rend_circ);
+
#endif /* TOR_HS_CLIENT_H */
1
0