From 27d7491f5a761c58fc687f0b816b80ff9f7a1a1d Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 12 Sep 2018 14:40:19 +0300 Subject: Introduce per-service HiddenServiceExportCircuitID torrc option. Moves code to a function, better viewed with --color-moved. --- src/feature/hs/hs_config.c | 20 +++++++++++++++++++- src/feature/hs/hs_service.c | 13 +++++++++++++ src/feature/hs/hs_service.h | 5 +++++ 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'src/feature') 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 -- cgit v1.2.3-54-g00ecf From 6069185bcc61fe797582b9b9826a5d52e4aabb22 Mon Sep 17 00:00:00 2001 From: George Kadianakis Date: Wed, 12 Sep 2018 15:52:41 +0300 Subject: 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(-) (limited to 'src/feature') 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; -- cgit v1.2.3-54-g00ecf From 9b511dc5d6a9e44bd8c8c644ad9445cab7cdafe2 Mon Sep 17 00:00:00 2001 From: Alexander Færøy Date: Sat, 15 Sep 2018 16:33:31 +0300 Subject: 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(-) (limited to 'src/feature') 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"); -- cgit v1.2.3-54-g00ecf From 8ecaf41003cc5b513cf26c4ae176d5043f5a6a93 Mon Sep 17 00:00:00 2001 From: Alexander Færøy Date: Thu, 20 Sep 2018 20:59:42 +0200 Subject: 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(-) (limited to 'src/feature') 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; } -- cgit v1.2.3-54-g00ecf