From be142194cd447a5e31836128c9166f8a592a1649 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 30 Jul 2018 16:10:23 -0700 Subject: [PATCH 01/11] Encode Circuit ID as src IP in Proxy Protocol for Opportunistic Onions --- src/core/or/connection_edge.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index ab3c14d2c3..a85419376f 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -617,6 +617,29 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) rep_hist_note_exit_stream_opened(conn->port); conn->state = EXIT_CONN_STATE_OPEN; + + /* Include Proxy Protocol header. */ + char buf[512]; + char dst_ipv6[39] = "::1"; + /* See RFC4193 regarding fc00::/7 */ + char src_ipv6_prefix[34] = "fc00:dead:beef:4dad:"; + /* TODO: retain virtual port and use as destination port */ + uint16_t dst_port = 443; + uint16_t src_port = 0; + uint32_t gid = 0; + + if (edge_conn->on_circuit != NULL) { + gid = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit)->global_identifier; + src_port = gid & 0x0000ffff; + } + + gid = (gid == 0) ? 1 : gid; + src_port = (src_port == 0) ? 1 : src_port; + + tor_snprintf(buf, sizeof(buf), "PROXY TCP6 %s:%x %s %d %d\r\n", + src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); + connection_buf_add(buf, strlen(buf), conn); + connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ if (connection_get_outbuf_len(conn)) /* in case there are any queued relay * cells */ From 27d7491f5a761c58fc687f0b816b80ff9f7a1a1d Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 12 Sep 2018 14:40:19 +0300 Subject: [PATCH 02/11] Introduce per-service HiddenServiceExportCircuitID torrc option. Moves code to a function, better viewed with --color-moved. --- changes/bug4700 | 5 ++++ src/app/config/config.c | 1 + src/core/or/connection_edge.c | 52 +++++++++++++++++++++-------------- src/feature/hs/hs_config.c | 20 +++++++++++++- src/feature/hs/hs_service.c | 13 +++++++++ src/feature/hs/hs_service.h | 5 ++++ 6 files changed, 75 insertions(+), 21 deletions(-) create mode 100644 changes/bug4700 diff --git a/changes/bug4700 b/changes/bug4700 new file mode 100644 index 0000000000..3c8d9b19b0 --- /dev/null +++ b/changes/bug4700 @@ -0,0 +1,5 @@ + o Minor features (onion services): + - Version 3 onion services can now use the per-service + HiddenServiceExportCircuitID option to differentiate client circuits by + using the HAProxy proxy protocol which assigns IP addresses to inbound client + circuits. Closes ticket 4700. Patch by Mahrud Sayrafi. diff --git a/src/app/config/config.c b/src/app/config/config.c index 1adeb75c98..13421f1033 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -457,6 +457,7 @@ static config_var_t option_vars_[] = { VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceNumIntroductionPoints", LINELIST_S, RendConfigLines, NULL), + VAR("HiddenServiceExportCircuitID", LINELIST_S, RendConfigLines, NULL), VAR("HiddenServiceStatistics", BOOL, HiddenServiceStatistics_option, "1"), V(HidServAuth, LINELIST, NULL), V(ClientOnionAuthDir, FILENAME, NULL), diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index a85419376f..8b333a6f49 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -597,6 +597,33 @@ connected_cell_format_payload(uint8_t *payload_out, return connected_payload_len; } +/* DOCDOCDOC */ +static void +send_ha_proxy_header(const edge_connection_t *edge_conn, + connection_t *conn) +{ + char buf[512]; + char dst_ipv6[39] = "::1"; + /* See RFC4193 regarding fc00::/7 */ + char src_ipv6_prefix[34] = "fc00:dead:beef:4dad:"; + /* TODO: retain virtual port and use as destination port */ + uint16_t dst_port = 443; + uint16_t src_port = 0; + uint32_t gid = 0; + + if (edge_conn->on_circuit != NULL) { + gid = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit)->global_identifier; + src_port = gid & 0x0000ffff; + } + + gid = (gid == 0) ? 1 : gid; + src_port = (src_port == 0) ? 1 : src_port; + + tor_snprintf(buf, sizeof(buf), "PROXY TCP6 %s:%x %s %d %d\r\n", + src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); + connection_buf_add(buf, strlen(buf), conn); +} + /** Connected handler for exit connections: start writing pending * data, deliver 'CONNECTED' relay cells as appropriate, and check * any pending data that may have been received. */ @@ -618,28 +645,13 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) conn->state = EXIT_CONN_STATE_OPEN; - /* Include Proxy Protocol header. */ - char buf[512]; - char dst_ipv6[39] = "::1"; - /* See RFC4193 regarding fc00::/7 */ - char src_ipv6_prefix[34] = "fc00:dead:beef:4dad:"; - /* TODO: retain virtual port and use as destination port */ - uint16_t dst_port = 443; - uint16_t src_port = 0; - uint32_t gid = 0; - - if (edge_conn->on_circuit != NULL) { - gid = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit)->global_identifier; - src_port = gid & 0x0000ffff; + /* If it's an onion service connection, we might want to include the proxy + * protocol header */ + if (edge_conn->hs_ident && + hs_service_exports_circuit_id(&edge_conn->hs_ident->identity_pk)) { + send_ha_proxy_header(edge_conn, conn); } - gid = (gid == 0) ? 1 : gid; - src_port = (src_port == 0) ? 1 : src_port; - - tor_snprintf(buf, sizeof(buf), "PROXY TCP6 %s:%x %s %d %d\r\n", - src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); - connection_buf_add(buf, strlen(buf), conn); - connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ if (connection_get_outbuf_len(conn)) /* in case there are any queued relay * cells */ diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index eaeb58829a..16bfe7c544 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -188,6 +188,11 @@ config_has_invalid_options(const config_line_t *line_, NULL /* End marker. */ }; + const char *opts_exclude_v2[] = { + "HiddenServiceExportCircuitID", + NULL /* End marker. */ + }; + /* Defining the size explicitly allows us to take advantage of the compiler * which warns us if we ever bump the max version but forget to grow this * array. The plus one is because we have a version 0 :). */ @@ -196,7 +201,7 @@ config_has_invalid_options(const config_line_t *line_, } exclude_lists[HS_VERSION_MAX + 1] = { { NULL }, /* v0. */ { NULL }, /* v1. */ - { NULL }, /* v2 */ + { opts_exclude_v2 }, /* v2 */ { opts_exclude_v3 }, /* v3. */ }; @@ -262,6 +267,7 @@ config_service_v3(const config_line_t *line_, hs_service_config_t *config) { int have_num_ip = 0; + bool export_circuit_id = false; /* just to detect duplicate options */ const char *dup_opt_seen = NULL; const config_line_t *line; @@ -288,6 +294,18 @@ config_service_v3(const config_line_t *line_, have_num_ip = 1; continue; } + if (!strcasecmp(line->key, "HiddenServiceExportCircuitID")) { + config->export_circuit_id = + (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok); + if (!ok || export_circuit_id) { + if (export_circuit_id) { + dup_opt_seen = line->key; + } + goto err; + } + export_circuit_id = true; + continue; + } } /* We do not load the key material for the service at this stage. This is diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index 30d23eb771..75d7cb75ed 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -3762,6 +3762,19 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ, return -1; } +/** Does the service with identity pubkey pk export the circuit IDs of + * its clients? */ +bool +hs_service_exports_circuit_id(const ed25519_public_key_t *pk) +{ + hs_service_t *service = find_service(hs_service_map, pk); + if (!service) { + return 0; + } + + return service->config.export_circuit_id; +} + /* Add to file_list every filename used by a configured hidden service, and to * dir_list every directory path used by a configured hidden service. This is * used by the sandbox subsystem to whitelist those. */ diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 735266071f..e541cb28b9 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -210,6 +210,9 @@ typedef struct hs_service_config_t { /* Is this service ephemeral? */ unsigned int is_ephemeral : 1; + + /* Does this service export the circuit ID of its clients? */ + bool export_circuit_id; } hs_service_config_t; /* Service state. */ @@ -316,6 +319,8 @@ void hs_service_upload_desc_to_dir(const char *encoded_desc, const ed25519_public_key_t *blinded_pk, const routerstatus_t *hsdir_rs); +bool hs_service_exports_circuit_id(const ed25519_public_key_t *pk); + #ifdef HS_SERVICE_PRIVATE #ifdef TOR_UNIT_TESTS From 5d34a8cbbb8ac4b6edd6800e10b5cb6dba1dcbcb Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 12 Sep 2018 14:43:23 +0300 Subject: [PATCH 03/11] Improve export_hs_client_circuit_id() function. - Change default values. - Beautify. - Documentation. --- src/core/or/connection_edge.c | 31 +++++++++++++++++-------------- src/core/or/connection_edge.h | 3 +++ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 8b333a6f49..9af03f7f23 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -597,31 +597,34 @@ connected_cell_format_payload(uint8_t *payload_out, return connected_payload_len; } -/* DOCDOCDOC */ -static void -send_ha_proxy_header(const edge_connection_t *edge_conn, - connection_t *conn) +/* This is an onion service client connection: Export the client circuit ID + * according to the HAProxy proxy protocol. */ +STATIC void +export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, + connection_t *conn) { - char buf[512]; - char dst_ipv6[39] = "::1"; + char *buf; + const char dst_ipv6[] = "::1"; /* See RFC4193 regarding fc00::/7 */ - char src_ipv6_prefix[34] = "fc00:dead:beef:4dad:"; + const char src_ipv6_prefix[] = "fc00:dead:beef:4dad:"; /* TODO: retain virtual port and use as destination port */ uint16_t dst_port = 443; - uint16_t src_port = 0; - uint32_t gid = 0; + uint16_t src_port = 1; /* default value */ + uint32_t gid = 0; /* default value */ + /* Generate a GID and source port for this client */ if (edge_conn->on_circuit != NULL) { gid = TO_ORIGIN_CIRCUIT(edge_conn->on_circuit)->global_identifier; src_port = gid & 0x0000ffff; } - gid = (gid == 0) ? 1 : gid; - src_port = (src_port == 0) ? 1 : src_port; + /* Build the string */ + tor_asprintf(&buf, "PROXY TCP6 %s:%x %s %d %d\r\n", + src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); - tor_snprintf(buf, sizeof(buf), "PROXY TCP6 %s:%x %s %d %d\r\n", - src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); connection_buf_add(buf, strlen(buf), conn); + + tor_free(buf); } /** Connected handler for exit connections: start writing pending @@ -649,7 +652,7 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) * protocol header */ if (edge_conn->hs_ident && hs_service_exports_circuit_id(&edge_conn->hs_ident->identity_pk)) { - send_ha_proxy_header(edge_conn, conn); + export_hs_client_circuit_id_haproxy(edge_conn, conn); } connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 24968b2778..adec4998e8 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -243,6 +243,9 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn, rewrite_result_t *out); STATIC int connection_ap_process_http_connect(entry_connection_t *conn); +STATIC void +export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, + connection_t *conn); #endif /* defined(CONNECTION_EDGE_PRIVATE) */ #endif /* !defined(TOR_CONNECTION_EDGE_H) */ From 6069185bcc61fe797582b9b9826a5d52e4aabb22 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 12 Sep 2018 15:52:41 +0300 Subject: [PATCH 04/11] Save original virtual port in edge conn HS ident. --- src/core/or/connection_edge.c | 8 ++++++-- src/feature/hs/hs_common.c | 5 +++++ src/feature/hs/hs_ident.h | 4 ++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 9af03f7f23..891e922171 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -607,8 +607,7 @@ export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, const char dst_ipv6[] = "::1"; /* See RFC4193 regarding fc00::/7 */ const char src_ipv6_prefix[] = "fc00:dead:beef:4dad:"; - /* TODO: retain virtual port and use as destination port */ - uint16_t dst_port = 443; + uint16_t dst_port = 0; uint16_t src_port = 1; /* default value */ uint32_t gid = 0; /* default value */ @@ -618,6 +617,11 @@ export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, src_port = gid & 0x0000ffff; } + /* Grab the original dest port from the hs ident */ + if (edge_conn->hs_ident) { + dst_port = edge_conn->hs_ident->orig_virtual_port; + } + /* Build the string */ tor_asprintf(&buf, "PROXY TCP6 %s:%x %s %d %d\r\n", src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index 12405a79cb..c36892e0f8 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -882,6 +882,11 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn) smartlist_free(matching_ports); if (chosen_port) { if (!(chosen_port->is_unix_addr)) { + /* save the original destination before we overwrite it */ + if (conn->hs_ident) { + conn->hs_ident->orig_virtual_port = TO_CONN(conn)->port; + } + /* Get a non-AF_UNIX connection ready for connection_exit_connect() */ tor_addr_copy(&TO_CONN(conn)->addr, &chosen_port->real_addr); TO_CONN(conn)->port = chosen_port->real_port; diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h index 92d15b0523..ab87d16d17 100644 --- a/src/feature/hs/hs_ident.h +++ b/src/feature/hs/hs_ident.h @@ -111,6 +111,10 @@ typedef struct hs_ident_edge_conn_t { * in the onion address. */ ed25519_public_key_t identity_pk; + /* The original virtual port that was used by the client to access the onion + * service, regardless of the internal port forwarding that might have + * happened on the service-side. */ + uint16_t orig_virtual_port; /* XXX: Client authorization. */ } hs_ident_edge_conn_t; From b2092f1ced891737d83915f027b6776882d256b5 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Sat, 15 Sep 2018 16:33:05 +0300 Subject: [PATCH 05/11] Add unittest for HiddenServiceExportCircuitID. Had to move a function to test helpers. --- src/core/or/or.h | 1 + src/test/test_extorport.c | 17 +-------- src/test/test_helpers.c | 19 +++++++++++ src/test/test_helpers.h | 3 ++ src/test/test_hs_service.c | 70 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/core/or/or.h b/src/core/or/or.h index eae0270124..efc124fa63 100644 --- a/src/core/or/or.h +++ b/src/core/or/or.h @@ -26,6 +26,7 @@ #include "lib/cc/compat_compiler.h" #include "lib/cc/torint.h" #include "lib/container/map.h" +#include "lib/container/buffers.h" #include "lib/container/smartlist.h" #include "lib/crypt_ops/crypto_cipher.h" #include "lib/crypt_ops/crypto_rsa.h" diff --git a/src/test/test_extorport.c b/src/test/test_extorport.c index ff987563c6..7babc81ee7 100644 --- a/src/test/test_extorport.c +++ b/src/test/test_extorport.c @@ -17,6 +17,7 @@ #include "core/or/or_connection_st.h" #include "test/test.h" +#include "test/test_helpers.h" #ifdef HAVE_SYS_STAT_H #include @@ -89,22 +90,6 @@ connection_write_to_buf_impl_replacement(const char *string, size_t len, buf_add(conn->outbuf, string, len); } -static char * -buf_get_contents(buf_t *buf, size_t *sz_out) -{ - char *out; - *sz_out = buf_datalen(buf); - if (*sz_out >= ULONG_MAX) - return NULL; /* C'mon, really? */ - out = tor_malloc(*sz_out + 1); - if (buf_get_bytes(buf, out, (unsigned long)*sz_out) != 0) { - tor_free(out); - return NULL; - } - out[*sz_out] = '\0'; /* Hopefully gratuitous. */ - return out; -} - static void test_ext_or_write_command(void *arg) { diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index c9138611d8..a10821956e 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -125,6 +125,25 @@ connection_write_to_buf_mock(const char *string, size_t len, buf_add(conn->outbuf, string, len); } +char * +buf_get_contents(buf_t *buf, size_t *sz_out) +{ + tor_assert(buf); + tor_assert(sz_out); + + char *out; + *sz_out = buf_datalen(buf); + if (*sz_out >= ULONG_MAX) + return NULL; /* C'mon, really? */ + out = tor_malloc(*sz_out + 1); + if (buf_get_bytes(buf, out, (unsigned long)*sz_out) != 0) { + tor_free(out); + return NULL; + } + out[*sz_out] = '\0'; /* Hopefully gratuitous. */ + return out; +} + /* Set up a fake origin circuit with the specified number of cells, * Return a pointer to the newly-created dummy circuit */ circuit_t * diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 3196c93e6b..72bf7f2f71 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -4,6 +4,8 @@ #ifndef TOR_TEST_HELPERS_H #define TOR_TEST_HELPERS_H +#define BUFFERS_PRIVATE + #include "core/or/or.h" const char *get_yesterday_date_str(void); @@ -18,6 +20,7 @@ void helper_setup_fake_routerlist(void); #define GET(path) "GET " path " HTTP/1.0\r\n\r\n" void connection_write_to_buf_mock(const char *string, size_t len, connection_t *conn, int compressed); +char *buf_get_contents(buf_t *buf, size_t *sz_out); int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, uint16_t family, tor_addr_t *out); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index bceeafd149..0a1c866d6b 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -10,6 +10,7 @@ #define CIRCUITLIST_PRIVATE #define CONFIG_PRIVATE #define CONNECTION_PRIVATE +#define CONNECTION_EDGE_PRIVATE #define CRYPTO_PRIVATE #define HS_COMMON_PRIVATE #define HS_SERVICE_PRIVATE @@ -33,6 +34,9 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/mainloop/connection.h" +#include "core/or/connection_edge.h" +#include "core/or/edge_connection_st.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/fs/dir.h" #include "feature/dirauth/dirvote.h" @@ -2003,6 +2007,70 @@ test_authorized_client_config_equal(void *arg) tor_free(config2); } +/** Test that client circuit ID gets correctly exported */ +static void +test_export_client_circuit_id(void *arg) +{ + origin_circuit_t *or_circ = NULL; + size_t sz; + char *cp1=NULL, *cp2=NULL; + connection_t *conn = NULL; + + (void) arg; + + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + + hs_service_init(); + + /* Create service */ + hs_service_t *service = helper_create_service(); + /* Check that export circuit ID detection works */ + service->config.export_circuit_id = false; + tt_int_op(0, OP_EQ, + hs_service_exports_circuit_id(&service->keys.identity_pk)); + service->config.export_circuit_id = true; + tt_int_op(1, OP_EQ, + hs_service_exports_circuit_id(&service->keys.identity_pk)); + + /* Create client connection */ + conn = test_conn_get_connection(AP_CONN_STATE_CIRCUIT_WAIT, CONN_TYPE_AP, 0); + + /* Create client edge conn hs_ident */ + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + edge_conn->hs_ident = hs_ident_edge_conn_new(&service->keys.identity_pk); + edge_conn->hs_ident->orig_virtual_port = 42; + + /* Create rend circuit */ + or_circ = origin_circuit_new(); + or_circ->base_.purpose = CIRCUIT_PURPOSE_C_REND_JOINED; + edge_conn->on_circuit = TO_CIRCUIT(or_circ); + or_circ->global_identifier = 666; + + /* Export circuit ID */ + export_hs_client_circuit_id_haproxy(edge_conn, conn); + + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::0:29a ::1 666 42\r\n"); + + /* Change circ GID and see that the reported circuit ID also changes */ + or_circ->global_identifier = 22; + + /* check changes */ + export_hs_client_circuit_id_haproxy(edge_conn, conn); + cp2 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_NE, cp2); + + done: + UNMOCK(connection_write_to_buf_impl_); + circuit_free_(TO_CIRCUIT(or_circ)); + connection_free_minimal(conn); + hs_service_free(service); + tor_free(cp1); + tor_free(cp2); +} + struct testcase_t hs_service_tests[] = { { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, NULL, NULL }, @@ -2044,6 +2112,8 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "authorized_client_config_equal", test_authorized_client_config_equal, TT_FORK, NULL, NULL }, + { "export_client_circuit_id", test_export_client_circuit_id, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; From e44e6a1857ff8ad39d3e298b512e8c8a1e513c99 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Thu, 13 Sep 2018 17:37:05 +0300 Subject: [PATCH 06/11] Add man page entry. --- doc/tor.1.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 869a8cedd7..abd1fdefa2 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2835,6 +2835,11 @@ The following options are used to configure a hidden service. not an authorization mechanism; it is instead meant to be a mild inconvenience to port-scanners.) (Default: 0) +[[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** **0**|**1**:: + If set to 1, then the onion service will use the HAProxy proxy protocol to + assign a unique IPv6 address (in an unused IPv6 range) to each inbound + client circuit. (Default: 0) + [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous circuit. The maximum value allowed is 65535. (Setting this to 0 will allow From 8f085841ef40f00bbc2bb146a2d555aba527738f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Fri, 14 Sep 2018 21:37:36 +0200 Subject: [PATCH 07/11] Encode the 32-bit Global Identifier as 2 x 16-bit in the IPv6 address. Without this patch we would encode the IPv6 address' last part as ::ffffffff instead of ::ffff:ffff when the GID is UINT32_MAX. See: https://bugs.torproject.org/4700 --- src/core/or/connection_edge.c | 6 ++++-- src/test/test_hs_service.c | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 891e922171..62d12f4986 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -623,8 +623,10 @@ export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, } /* Build the string */ - tor_asprintf(&buf, "PROXY TCP6 %s:%x %s %d %d\r\n", - src_ipv6_prefix, gid, dst_ipv6, src_port, dst_port); + tor_asprintf(&buf, "PROXY TCP6 %s:%x:%x %s %d %d\r\n", + src_ipv6_prefix, + gid >> 16, gid & 0x0000ffff, + dst_ipv6, src_port, dst_port); connection_buf_add(buf, strlen(buf), conn); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 0a1c866d6b..2b8d6e597a 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -2061,6 +2061,32 @@ test_export_client_circuit_id(void *arg) export_hs_client_circuit_id_haproxy(edge_conn, conn); cp2 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_NE, cp2); + tor_free(cp1); + + /* Check that GID with UINT32_MAX works. */ + or_circ->global_identifier = UINT32_MAX; + + export_hs_client_circuit_id_haproxy(edge_conn, conn); + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n"); + tor_free(cp1); + + /* Check that GID with UINT16_MAX works. */ + or_circ->global_identifier = UINT16_MAX; + + export_hs_client_circuit_id_haproxy(edge_conn, conn); + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 42\r\n"); + tor_free(cp1); + + /* Check that GID with UINT16_MAX + 7 works. */ + or_circ->global_identifier = UINT16_MAX + 7; + + export_hs_client_circuit_id_haproxy(edge_conn, conn); + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 42\r\n"); done: UNMOCK(connection_write_to_buf_impl_); From 9b511dc5d6a9e44bd8c8c644ad9445cab7cdafe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sat, 15 Sep 2018 16:33:31 +0300 Subject: [PATCH 08/11] Change HiddenServiceExportCircuitID to take a string parameter: the protocol. This patch changes HiddenServiceExportCircuitID so instead of being a boolean it takes a string, which is the protocol. Currently only the 'haproxy' protocol is defined. See: https://bugs.torproject.org/4700 --- doc/tor.1.txt | 8 ++++---- src/core/or/connection_edge.c | 20 +++++++++++++------- src/core/or/connection_edge.h | 8 ++++++-- src/feature/hs/hs_config.c | 29 +++++++++++++++++++++++++++-- src/feature/hs/hs_service.c | 6 +++--- src/feature/hs/hs_service.h | 14 ++++++++++++-- src/test/test_hs_service.c | 19 ++++++++++++------- 7 files changed, 77 insertions(+), 27 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index abd1fdefa2..d260eec10d 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2835,10 +2835,10 @@ The following options are used to configure a hidden service. not an authorization mechanism; it is instead meant to be a mild inconvenience to port-scanners.) (Default: 0) -[[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** **0**|**1**:: - If set to 1, then the onion service will use the HAProxy proxy protocol to - assign a unique IPv6 address (in an unused IPv6 range) to each inbound - client circuit. (Default: 0) +[[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** __protocol__:: + The onion service will use the given protocol to expose the global circuit + identifier of each inbound client circuit via the selected protocol. The only + protocol supported right now \'haproxy\'. This option is only for v3 services. [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 62d12f4986..0eff007e36 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -600,10 +600,15 @@ connected_cell_format_payload(uint8_t *payload_out, /* This is an onion service client connection: Export the client circuit ID * according to the HAProxy proxy protocol. */ STATIC void -export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, - connection_t *conn) +export_hs_client_circuit_id(const edge_connection_t *edge_conn, + connection_t *conn, + hs_circuit_id_protocol_t protocol) { - char *buf; + /* We only support HAProxy right now. */ + if (protocol != HS_CIRCUIT_ID_PROTOCOL_HAPROXY) + return; + + char *buf = NULL; const char dst_ipv6[] = "::1"; /* See RFC4193 regarding fc00::/7 */ const char src_ipv6_prefix[] = "fc00:dead:beef:4dad:"; @@ -655,10 +660,11 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) conn->state = EXIT_CONN_STATE_OPEN; /* If it's an onion service connection, we might want to include the proxy - * protocol header */ - if (edge_conn->hs_ident && - hs_service_exports_circuit_id(&edge_conn->hs_ident->identity_pk)) { - export_hs_client_circuit_id_haproxy(edge_conn, conn); + * protocol header: */ + if (edge_conn->hs_ident) { + hs_circuit_id_protocol_t circuit_id_protocol = + hs_service_exports_circuit_id(&edge_conn->hs_ident->identity_pk); + export_hs_client_circuit_id(edge_conn, conn, circuit_id_protocol); } connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index adec4998e8..c5ad3128a2 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -14,6 +14,8 @@ #include "lib/testsupport/testsupport.h" +#include "feature/hs/hs_service.h" + edge_connection_t *TO_EDGE_CONN(connection_t *); entry_connection_t *TO_ENTRY_CONN(connection_t *); entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *); @@ -244,8 +246,10 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn, STATIC int connection_ap_process_http_connect(entry_connection_t *conn); STATIC void -export_hs_client_circuit_id_haproxy(const edge_connection_t *edge_conn, - connection_t *conn); +export_hs_client_circuit_id(const edge_connection_t *edge_conn, + connection_t *conn, + hs_circuit_id_protocol_t protocol); + #endif /* defined(CONNECTION_EDGE_PRIVATE) */ #endif /* !defined(TOR_CONNECTION_EDGE_H) */ diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 16bfe7c544..2378a4d3b2 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -145,6 +145,31 @@ helper_parse_uint64(const char *opt, const char *value, uint64_t min, return ret; } +/** Helper function: Given a configuration option and its value, parse the + * value as a hs_circuit_id_protocol_t. On success, ok is set to 1 and ret is + * the parse value. On error, ok is set to 0 and the "none" + * hs_circuit_id_protocol_t is returned. This function logs on error. */ +static hs_circuit_id_protocol_t +helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok) +{ + tor_assert(value); + tor_assert(ok); + + hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE; + *ok = 0; + + if (! strcasecmp(value, "haproxy")) { + *ok = 1; + ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; + } else { + log_warn(LD_CONFIG, "%s must be 'haproxy'.", key); + goto err; + } + + err: + return ret; +} + /* Return the service version by trying to learn it from the key on disk if * any. If nothing is found, the current service configured version is * returned. */ @@ -295,8 +320,8 @@ config_service_v3(const config_line_t *line_, continue; } if (!strcasecmp(line->key, "HiddenServiceExportCircuitID")) { - config->export_circuit_id = - (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok); + config->circuit_id_protocol = + helper_parse_circuit_id_protocol(line->key, line->value, &ok); if (!ok || export_circuit_id) { if (export_circuit_id) { dup_opt_seen = line->key; diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index 75d7cb75ed..e87cb990f5 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -3764,15 +3764,15 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ, /** Does the service with identity pubkey pk export the circuit IDs of * its clients? */ -bool +hs_circuit_id_protocol_t hs_service_exports_circuit_id(const ed25519_public_key_t *pk) { hs_service_t *service = find_service(hs_service_map, pk); if (!service) { - return 0; + return HS_CIRCUIT_ID_PROTOCOL_NONE; } - return service->config.export_circuit_id; + return service->config.circuit_id_protocol; } /* Add to file_list every filename used by a configured hidden service, and to diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index e541cb28b9..6fb15b9d37 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -161,6 +161,15 @@ typedef struct hs_service_authorized_client_t { curve25519_public_key_t client_pk; } hs_service_authorized_client_t; +/** Which protocol to use for exporting HS client circuit ID. */ +typedef enum { + /** Don't expose the circuit id. */ + HS_CIRCUIT_ID_PROTOCOL_NONE, + + /** Use the HAProxy proxy protocol. */ + HS_CIRCUIT_ID_PROTOCOL_HAPROXY +} hs_circuit_id_protocol_t; + /* Service configuration. The following are set from the torrc options either * set by the configuration file or by the control port. Nothing else should * change those values. */ @@ -212,7 +221,7 @@ typedef struct hs_service_config_t { unsigned int is_ephemeral : 1; /* Does this service export the circuit ID of its clients? */ - bool export_circuit_id; + hs_circuit_id_protocol_t circuit_id_protocol; } hs_service_config_t; /* Service state. */ @@ -319,7 +328,8 @@ void hs_service_upload_desc_to_dir(const char *encoded_desc, const ed25519_public_key_t *blinded_pk, const routerstatus_t *hsdir_rs); -bool hs_service_exports_circuit_id(const ed25519_public_key_t *pk); +hs_circuit_id_protocol_t +hs_service_exports_circuit_id(const ed25519_public_key_t *pk); #ifdef HS_SERVICE_PRIVATE diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 2b8d6e597a..955bcc8aff 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -2025,10 +2025,10 @@ test_export_client_circuit_id(void *arg) /* Create service */ hs_service_t *service = helper_create_service(); /* Check that export circuit ID detection works */ - service->config.export_circuit_id = false; + service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE; tt_int_op(0, OP_EQ, hs_service_exports_circuit_id(&service->keys.identity_pk)); - service->config.export_circuit_id = true; + service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; tt_int_op(1, OP_EQ, hs_service_exports_circuit_id(&service->keys.identity_pk)); @@ -2047,7 +2047,8 @@ test_export_client_circuit_id(void *arg) or_circ->global_identifier = 666; /* Export circuit ID */ - export_hs_client_circuit_id_haproxy(edge_conn, conn); + export_hs_client_circuit_id(edge_conn, conn, + service->config.circuit_id_protocol); /* Check contents */ cp1 = buf_get_contents(conn->outbuf, &sz); @@ -2058,7 +2059,8 @@ test_export_client_circuit_id(void *arg) or_circ->global_identifier = 22; /* check changes */ - export_hs_client_circuit_id_haproxy(edge_conn, conn); + export_hs_client_circuit_id(edge_conn, conn, + service->config.circuit_id_protocol); cp2 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_NE, cp2); tor_free(cp1); @@ -2066,7 +2068,8 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT32_MAX works. */ or_circ->global_identifier = UINT32_MAX; - export_hs_client_circuit_id_haproxy(edge_conn, conn); + export_hs_client_circuit_id(edge_conn, conn, + service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n"); @@ -2075,7 +2078,8 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT16_MAX works. */ or_circ->global_identifier = UINT16_MAX; - export_hs_client_circuit_id_haproxy(edge_conn, conn); + export_hs_client_circuit_id(edge_conn, conn, + service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 42\r\n"); @@ -2084,7 +2088,8 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT16_MAX + 7 works. */ or_circ->global_identifier = UINT16_MAX + 7; - export_hs_client_circuit_id_haproxy(edge_conn, conn); + export_hs_client_circuit_id(edge_conn, conn, + service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 42\r\n"); From 3477a73af99eb72f8374928fdc2fab4858485219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sat, 15 Sep 2018 22:17:57 +0200 Subject: [PATCH 09/11] Add proxy headers as early as possible. This patch moves the logic that adds the proxy headers to an earlier point in the exit connection lifetime, which ensures that the application data cannot be written to the outbuf before the proxy header is added. See: https://bugs.torproject.org/4700 --- src/core/or/connection_edge.c | 21 ++++++++++----------- src/core/or/connection_edge.h | 3 +-- src/test/test_hs_service.c | 15 +++++---------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 0eff007e36..ab2acaa39c 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -600,8 +600,7 @@ connected_cell_format_payload(uint8_t *payload_out, /* This is an onion service client connection: Export the client circuit ID * according to the HAProxy proxy protocol. */ STATIC void -export_hs_client_circuit_id(const edge_connection_t *edge_conn, - connection_t *conn, +export_hs_client_circuit_id(edge_connection_t *edge_conn, hs_circuit_id_protocol_t protocol) { /* We only support HAProxy right now. */ @@ -633,7 +632,7 @@ export_hs_client_circuit_id(const edge_connection_t *edge_conn, gid >> 16, gid & 0x0000ffff, dst_ipv6, src_port, dst_port); - connection_buf_add(buf, strlen(buf), conn); + connection_buf_add(buf, strlen(buf), TO_CONN(edge_conn)); tor_free(buf); } @@ -659,14 +658,6 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) conn->state = EXIT_CONN_STATE_OPEN; - /* If it's an onion service connection, we might want to include the proxy - * protocol header: */ - if (edge_conn->hs_ident) { - hs_circuit_id_protocol_t circuit_id_protocol = - hs_service_exports_circuit_id(&edge_conn->hs_ident->identity_pk); - export_hs_client_circuit_id(edge_conn, conn, circuit_id_protocol); - } - connection_watch_events(conn, READ_EVENT); /* stop writing, keep reading */ if (connection_get_outbuf_len(conn)) /* in case there are any queued relay * cells */ @@ -3452,6 +3443,14 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) hs_inc_rdv_stream_counter(origin_circ); + /* If it's an onion service connection, we might want to include the proxy + * protocol header: */ + if (conn->hs_ident) { + hs_circuit_id_protocol_t circuit_id_protocol = + hs_service_exports_circuit_id(&conn->hs_ident->identity_pk); + export_hs_client_circuit_id(conn, circuit_id_protocol); + } + /* Connect tor to the hidden service destination. */ connection_exit_connect(conn); diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index c5ad3128a2..5b694428de 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -246,8 +246,7 @@ STATIC void connection_ap_handshake_rewrite(entry_connection_t *conn, STATIC int connection_ap_process_http_connect(entry_connection_t *conn); STATIC void -export_hs_client_circuit_id(const edge_connection_t *edge_conn, - connection_t *conn, +export_hs_client_circuit_id(edge_connection_t *edge_conn, hs_circuit_id_protocol_t protocol); #endif /* defined(CONNECTION_EDGE_PRIVATE) */ diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 955bcc8aff..dace2b63d8 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -2047,8 +2047,7 @@ test_export_client_circuit_id(void *arg) or_circ->global_identifier = 666; /* Export circuit ID */ - export_hs_client_circuit_id(edge_conn, conn, - service->config.circuit_id_protocol); + export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); /* Check contents */ cp1 = buf_get_contents(conn->outbuf, &sz); @@ -2059,8 +2058,7 @@ test_export_client_circuit_id(void *arg) or_circ->global_identifier = 22; /* check changes */ - export_hs_client_circuit_id(edge_conn, conn, - service->config.circuit_id_protocol); + export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); cp2 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_NE, cp2); tor_free(cp1); @@ -2068,8 +2066,7 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT32_MAX works. */ or_circ->global_identifier = UINT32_MAX; - export_hs_client_circuit_id(edge_conn, conn, - service->config.circuit_id_protocol); + export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n"); @@ -2078,8 +2075,7 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT16_MAX works. */ or_circ->global_identifier = UINT16_MAX; - export_hs_client_circuit_id(edge_conn, conn, - service->config.circuit_id_protocol); + export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 42\r\n"); @@ -2088,8 +2084,7 @@ test_export_client_circuit_id(void *arg) /* Check that GID with UINT16_MAX + 7 works. */ or_circ->global_identifier = UINT16_MAX + 7; - export_hs_client_circuit_id(edge_conn, conn, - service->config.circuit_id_protocol); + export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); cp1 = buf_get_contents(conn->outbuf, &sz); tt_str_op(cp1, OP_EQ, "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 42\r\n"); From 8ecaf41003cc5b513cf26c4ae176d5043f5a6a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Thu, 20 Sep 2018 20:59:42 +0200 Subject: [PATCH 10/11] Support 'none' in torrc for HiddenServiceExportCircuitID. See: https://bugs.torproject.org/4700 --- doc/tor.1.txt | 3 ++- src/feature/hs/hs_config.c | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index d260eec10d..df298e1d77 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2838,7 +2838,8 @@ The following options are used to configure a hidden service. [[HiddenServiceExportCircuitID]] **HiddenServiceExportCircuitID** __protocol__:: The onion service will use the given protocol to expose the global circuit identifier of each inbound client circuit via the selected protocol. The only - protocol supported right now \'haproxy\'. This option is only for v3 services. + protocol supported right now \'haproxy\'. This option is only for v3 + services. (Default: none) [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index 2378a4d3b2..93d7403dfb 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -161,8 +161,11 @@ helper_parse_circuit_id_protocol(const char *key, const char *value, int *ok) if (! strcasecmp(value, "haproxy")) { *ok = 1; ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; + } else if (! strcasecmp(value, "none")) { + *ok = 1; + ret = HS_CIRCUIT_ID_PROTOCOL_NONE; } else { - log_warn(LD_CONFIG, "%s must be 'haproxy'.", key); + log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", key); goto err; } From 03ef4ec466fd7f1de097a7b0244ca5bc8cc32b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Thu, 20 Sep 2018 21:15:25 +0200 Subject: [PATCH 11/11] Document the haproxy option of HiddenServiceExportCircuitID. See: https://bugs.torproject.org/4700 --- doc/tor.1.txt | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index df298e1d77..124b01053a 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2839,7 +2839,28 @@ The following options are used to configure a hidden service. The onion service will use the given protocol to expose the global circuit identifier of each inbound client circuit via the selected protocol. The only protocol supported right now \'haproxy\'. This option is only for v3 - services. (Default: none) + services. (Default: none) + + + + The haproxy option works in the following way: when the feature is + enabled, the Tor process will write a header line when a client is connecting + to the onion service. The header will look like this: + + + + "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 42\r\n" + + + + We encode the "global circuit identifier" as the last 32-bits of the first + IPv6 address. All other values in the header can safely be ignored. You can + compute the global circuit identifier using the following formula given the + IPv6 address "fc00:dead:beef:4dad::AABB:CCDD": + + + + global_circuit_id = (0xAA << 24) + (0xBB << 16) + (0xCC << 8) + 0xDD; + + + + In the case above, where the last 32-bit is 0xffffffff, the global circuit + identifier would be 4294967295. You can use this value together with Tor's + control port where it is possible to terminate a circuit given the global + circuit identifier. For more information about this see controls-spec.txt. + + + + The HAProxy version 1 proxy protocol is described in detail at + https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt [[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__:: The maximum number of simultaneous streams (connections) per rendezvous