From c666098920191a240416299d130cfc65b406d875 Mon Sep 17 00:00:00 2001 From: Suphanat Chunhapanya Date: Tue, 14 Dec 2021 15:15:01 +0800 Subject: [PATCH 1/3] Add an optional flag to export circuit id per port Previously we have HiddenServiceExportCircuitID which allows us to export the circuit id using haproxy. However, such directive is applied globally in the service. We need a way to export the circuit id only for some virtual ports. We can do that by adding an optional flag to the HiddenServicePort directive to specify that we will export the circuit id for such port. The flag in HiddenServicePort will override the one in HiddenServiceExportCircuitID, if it's not none. But it will use the protocol specified in HiddenServiceExportCircuitID, if it's otherwise. --- doc/man/tor.1.txt | 10 +++++-- src/core/or/connection_edge.c | 4 +-- src/feature/hs/hs_common.c | 51 ++++++++++++++++++++++++++++++----- src/feature/hs/hs_common.h | 13 +++++++++ src/feature/hs/hs_config.c | 34 +++-------------------- src/feature/hs/hs_ident.h | 3 +++ src/feature/hs/hs_service.c | 19 +++++-------- src/feature/hs/hs_service.h | 12 --------- src/test/test_hs_config.c | 4 +-- src/test/test_hs_service.c | 6 ----- 10 files changed, 80 insertions(+), 76 deletions(-) diff --git a/doc/man/tor.1.txt b/doc/man/tor.1.txt index 1814801b710..0b89f15b7cb 100644 --- a/doc/man/tor.1.txt +++ b/doc/man/tor.1.txt @@ -3421,7 +3421,7 @@ The next section describes the per service options that can only be set Number of introduction points the hidden service will have. You can't have more than 20. (Default: 3) -[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__]:: +[[HiddenServicePort]] **HiddenServicePort** __VIRTPORT__ [__TARGET__ [__EXPORT-CIRCUIT-ID-PROTOCOL__]]:: Configure a virtual port VIRTPORT for a hidden service. You may use this option multiple times; each time applies to the service using the most recent HiddenServiceDir. By default, this option maps the virtual port to @@ -3431,7 +3431,13 @@ The next section describes the per service options that can only be set paths may be quoted, and may use standard C escapes.) You may also have multiple lines with the same VIRTPORT: when a user connects to that VIRTPORT, one of the TARGETs from those lines will be - chosen at random. Note that address-port pairs have to be comma-separated. + chosen at random. Note that address-port pairs have to be + comma-separated. + + + + You also may specify the export circuit id protocol which is similar to + the one specified in **HiddenServiceExportCircuitID** but the procool + will be applied only to this port instead of being applied globally + in the service. [[HiddenServiceVersion]] **HiddenServiceVersion** **3**:: A list of rendezvous service descriptor versions to publish for the hidden diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index ea4bf00735a..2d3a5667f0a 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -3881,9 +3881,7 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) /* 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); + export_hs_client_circuit_id(conn, conn->hs_ident->circuit_id_protocol); } /* Connect tor to the hidden service destination. */ diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index c9195c29347..7b0da8d4552 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -640,6 +640,8 @@ hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn) /* There is always a connection identifier at this point. Regardless of a * Unix or TCP port, note the virtual port. */ conn->hs_ident->orig_virtual_port = chosen_port->virtual_port; + /* Note the export circuit id protocol to the connection. */ + conn->hs_ident->circuit_id_protocol = chosen_port->circuit_id_protocol; } if (!(chosen_port->is_unix_addr)) { @@ -677,7 +679,8 @@ hs_port_config_new(const char *socket_path) * the provided separator and returns a new hs_port_config_t, * or NULL and an optional error string on failure. * - * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)? + * The format is: VirtualPort SEP ((IP|RealPort|IP:RealPort|'socket':path) + * SEP (ExportCircuitIdProtocol)?)? * * IP defaults to 127.0.0.1; RealPort defaults to VirtualPort. */ @@ -691,6 +694,7 @@ hs_parse_port_config(const char *string, const char *sep, uint16_t p; tor_addr_t addr; hs_port_config_t *result = NULL; + hs_circuit_id_protocol_t circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE; unsigned int is_unix_addr = 0; const char *socket_path = NULL; char *err_msg = NULL; @@ -729,12 +733,6 @@ hs_parse_port_config(const char *string, const char *sep, goto err; } - if (rest && strlen(rest)) { - err_msg = tor_strdup("HiddenServicePort parse error: invalid port " - "mapping"); - goto err; - } - if (is_unix) { socket_path = addrport; is_unix_addr = 1; @@ -757,12 +755,24 @@ hs_parse_port_config(const char *string, const char *sep, } tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */ } + + if (rest && strlen(rest)) { + int ok; + circuit_id_protocol = + hs_parse_circuit_id_protocol(rest, &ok); + if (!ok) { + err_msg = tor_strdup("HiddenServicePort parse error: export circuit " + "id protocol must be 'haproxy' or 'none'."); + goto err; + } + } } /* Allow room for unix_addr */ result = hs_port_config_new(socket_path); result->virtual_port = virtport; result->is_unix_addr = is_unix_addr; + result->circuit_id_protocol = circuit_id_protocol; if (!is_unix_addr) { result->real_port = realport; tor_addr_copy(&result->real_addr, &addr); @@ -782,6 +792,33 @@ hs_parse_port_config(const char *string, const char *sep, return result; } +/** Given a configuration string, 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. */ +hs_circuit_id_protocol_t +hs_parse_circuit_id_protocol(const char *protocol_str, int *ok) +{ + tor_assert(protocol_str); + tor_assert(ok); + + hs_circuit_id_protocol_t ret = HS_CIRCUIT_ID_PROTOCOL_NONE; + *ok = 0; + + if (! strcasecmp(protocol_str, "haproxy")) { + *ok = 1; + ret = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; + } else if (! strcasecmp(protocol_str, "none")) { + *ok = 1; + ret = HS_CIRCUIT_ID_PROTOCOL_NONE; + } else { + goto err; + } + + err: + return ret; +} + /** Release all storage held in a hs_port_config_t. */ void hs_port_config_free_(hs_port_config_t *p) diff --git a/src/feature/hs/hs_common.h b/src/feature/hs/hs_common.h index a7a8f23a3ca..981e01c6e51 100644 --- a/src/feature/hs/hs_common.h +++ b/src/feature/hs/hs_common.h @@ -145,6 +145,15 @@ typedef enum { RSAE_OKAY = 0 /**< Service added as expected */ } hs_service_add_ephemeral_status_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; + /** Represents the mapping from a virtual port of a rendezvous service to a * real port on some IP. */ typedef struct hs_port_config_t { @@ -156,6 +165,8 @@ typedef struct hs_port_config_t { uint16_t real_port; /** The outgoing IPv4 or IPv6 address to use, if !is_unix_addr */ tor_addr_t real_addr; + /** Does this port export the circuit ID of its clients? */ + hs_circuit_id_protocol_t circuit_id_protocol; /** The socket path to connect to, if is_unix_addr */ char unix_addr[FLEXIBLE_ARRAY_MEMBER]; } hs_port_config_t; @@ -241,6 +252,8 @@ void hs_purge_last_hid_serv_requests(void); int hs_set_conn_addr_port(const smartlist_t *ports, edge_connection_t *conn); hs_port_config_t *hs_parse_port_config(const char *string, const char *sep, char **err_msg_out); +hs_circuit_id_protocol_t hs_parse_circuit_id_protocol(const char *protocol_str, + int *ok); void hs_port_config_free_(hs_port_config_t *p); #define hs_port_config_free(p) \ FREE_AND_NULL(hs_port_config_t, hs_port_config_free_, (p)) diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c index a76893fe1a4..677c5724ceb 100644 --- a/src/feature/hs/hs_config.c +++ b/src/feature/hs/hs_config.c @@ -177,34 +177,6 @@ check_value_oob(int i, const char *name, int low, int high) #define CHECK_OOB(opts, name, low, high) \ check_value_oob((opts)->name, #name, (low), (high)) -/** 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 if (! strcasecmp(value, "none")) { - *ok = 1; - ret = HS_CIRCUIT_ID_PROTOCOL_NONE; - } else { - log_warn(LD_CONFIG, "%s must be 'haproxy' or 'none'.", 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. */ @@ -351,10 +323,10 @@ config_service_v3(const hs_opts_t *hs_opts, if (hs_opts->HiddenServiceExportCircuitID) { int ok; config->circuit_id_protocol = - helper_parse_circuit_id_protocol("HiddenServcieExportCircuitID", - hs_opts->HiddenServiceExportCircuitID, - &ok); + hs_parse_circuit_id_protocol(hs_opts->HiddenServiceExportCircuitID, &ok); if (!ok) { + log_warn(LD_CONFIG, "HiddenServiceExportCircuitID must be " + "'haproxy' or 'none'."); goto err; } } diff --git a/src/feature/hs/hs_ident.h b/src/feature/hs/hs_ident.h index cb1249cbdcc..4e47b3e40d3 100644 --- a/src/feature/hs/hs_ident.h +++ b/src/feature/hs/hs_ident.h @@ -109,6 +109,9 @@ typedef struct hs_ident_edge_conn_t { * service, regardless of the internal port forwarding that might have * happened on the service-side. */ uint16_t orig_virtual_port; + + /** The export circuit id protocol that is used in this connection. */ + hs_circuit_id_protocol_t circuit_id_protocol; /* XXX: Client authorization. */ } hs_ident_edge_conn_t; diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index 9b7b5901404..4e271f8b107 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -3998,6 +3998,12 @@ hs_service_set_conn_addr_port(const origin_circuit_t *circ, goto err_no_close; } + /* If the the export circuit id protocol is none, looks at + * the HiddenServiceExportCircuitID config instead. */ + if (conn->hs_ident->circuit_id_protocol == HS_CIRCUIT_ID_PROTOCOL_NONE) { + conn->hs_ident->circuit_id_protocol = service->config.circuit_id_protocol; + } + /* Success. */ return 0; err_close: @@ -4008,19 +4014,6 @@ 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? */ -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 HS_CIRCUIT_ID_PROTOCOL_NONE; - } - - return service->config.circuit_id_protocol; -} - /** 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 allowlist those. */ diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index c48f4702457..2bb48e8237b 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -190,15 +190,6 @@ 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. */ @@ -380,9 +371,6 @@ void hs_service_upload_desc_to_dir(const char *encoded_desc, const ed25519_public_key_t *blinded_pk, const routerstatus_t *hsdir_rs); -hs_circuit_id_protocol_t -hs_service_exports_circuit_id(const ed25519_public_key_t *pk); - void hs_service_dump_stats(int severity); void hs_service_circuit_cleanup_on_close(const circuit_t *circ); diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c index 74f823f897f..b77b5252e89 100644 --- a/src/test/test_hs_config.c +++ b/src/test/test_hs_config.c @@ -151,8 +151,8 @@ test_invalid_service(void *arg) setup_full_capture_of_logs(LOG_WARN); ret = helper_config_service(conf, 1); tt_int_op(ret, OP_EQ, -1); - expect_log_msg_containing("HiddenServicePort parse error: " - "invalid port mapping"); + expect_log_msg_containing("HiddenServicePort parse error: export circuit " + "id protocol must be 'haproxy' or 'none'."); teardown_capture_of_logs(); } diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 33a3f279c67..480ff7739ad 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -2109,13 +2109,7 @@ 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.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.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; - 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); From d42def31994745165aac253601fdd447e5d5cf6a Mon Sep 17 00:00:00 2001 From: Suphanat Chunhapanya Date: Wed, 15 Dec 2021 18:06:47 +0800 Subject: [PATCH 2/3] Export circuit id config test in HiddenServicePort --- src/test/test_controller.c | 29 +++++++++++++++++++++++++++++ src/test/test_hs_config.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 85042e9ec26..362c46c7974 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -366,24 +366,28 @@ test_hs_parse_port_config(void *arg) /* Test "VIRTPORT" only. */ cfg = hs_parse_port_config("80", sep, &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test "VIRTPORT,TARGET" (Target is port). */ hs_port_config_free(cfg); cfg = hs_parse_port_config("80,8080", sep, &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test "VIRTPORT,TARGET" (Target is IPv4:port). */ hs_port_config_free(cfg); cfg = hs_parse_port_config("80,192.0.2.1:8080", sep, &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); /* Test "VIRTPORT,TARGET" (Target is IPv6:port). */ hs_port_config_free(cfg); cfg = hs_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); hs_port_config_free(cfg); cfg = NULL; @@ -411,6 +415,7 @@ test_hs_parse_port_config(void *arg) cfg = hs_parse_port_config("100 unix:\"/tmp/foo bar\"", " ", &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); hs_port_config_free(cfg); cfg = NULL; @@ -420,6 +425,7 @@ test_hs_parse_port_config(void *arg) cfg = hs_parse_port_config("100 unix:\"/tmp/foo bar\"", " ", &err_msg); tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); tt_ptr_op(err_msg, OP_EQ, NULL); hs_port_config_free(cfg); cfg = NULL; @@ -450,6 +456,29 @@ test_hs_parse_port_config(void *arg) "in hidden service port configuration."); tor_free(err_msg); + /* bad export circuit id protocol */ + cfg = hs_parse_port_config("100 1000 badprotocol", " ", &err_msg); + tt_ptr_op(cfg, OP_EQ, NULL); + tt_str_op(err_msg, OP_EQ, "HiddenServicePort parse error: export circuit " + "id protocol must be 'haproxy' or 'none'."); + tor_free(err_msg); + + /* none export circuit id protocol */ + cfg = hs_parse_port_config("100 1000 none", " ", &err_msg); + tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_NONE); + tt_ptr_op(err_msg, OP_EQ, NULL); + hs_port_config_free(cfg); + cfg = NULL; + + /* haproxy export circuit id protocol */ + cfg = hs_parse_port_config("100 1000 haproxy", " ", &err_msg); + tt_assert(cfg); + tt_int_op(cfg->circuit_id_protocol, OP_EQ, HS_CIRCUIT_ID_PROTOCOL_HAPROXY); + tt_ptr_op(err_msg, OP_EQ, NULL); + hs_port_config_free(cfg); + cfg = NULL; + /* Wrong target address and port separation */ cfg = hs_parse_port_config("80,127.0.0.1 1234", sep, &err_msg); diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c index b77b5252e89..2b937129394 100644 --- a/src/test/test_hs_config.c +++ b/src/test/test_hs_config.c @@ -156,6 +156,20 @@ test_invalid_service(void *arg) teardown_capture_of_logs(); } + /* Bad export circuit id protocol. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 80 127.0.0.1 badprotocol\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServicePort parse error: export circuit " + "id protocol must be 'haproxy' or 'none'."); + teardown_capture_of_logs(); + } + /* Out of order directives. */ { const char *conf = @@ -190,6 +204,24 @@ test_valid_service(void *arg) tt_int_op(ret, OP_EQ, 0); } + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 81 127.0.0.1:81 haproxy\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 81 127.0.0.1:81 none\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + done: ; } From 1c12404a7635278596798332310573ef615fe212 Mon Sep 17 00:00:00 2001 From: Suphanat Chunhapanya Date: Thu, 16 Dec 2021 00:31:27 +0800 Subject: [PATCH 3/3] Export circuit id logic test --- src/core/or/connection_edge.c | 8 +- src/core/or/connection_edge.h | 5 +- src/test/test_hs_service.c | 277 ++++++++++++++++++++++++++++------ 3 files changed, 240 insertions(+), 50 deletions(-) diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index 2d3a5667f0a..3b8416a14a6 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -936,7 +936,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 +static void export_hs_client_circuit_id(edge_connection_t *edge_conn, hs_circuit_id_protocol_t protocol) { @@ -3810,7 +3810,7 @@ begin_cell_parse(const cell_t *cell, begin_cell_t *bcell, * connection, attach it to the circ and connect it. Return 0 on success * or END_CIRC_AT_ORIGIN if we can't find the requested hidden service port * where the caller should close the circuit. */ -static int +STATIC int handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) { int ret; @@ -4185,8 +4185,8 @@ network_reentry_is_allowed(void) * address, but only if it's a general exit stream. (Rendezvous * streams must not reveal what IP they connected to.) */ -void -connection_exit_connect(edge_connection_t *edge_conn) +MOCK_IMPL(void, +connection_exit_connect,(edge_connection_t *edge_conn)) { const tor_addr_t *addr; uint16_t port; diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 966a9391d89..c53954109c6 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -129,7 +129,7 @@ void connection_ap_handshake_socks_resolved_addr(entry_connection_t *conn, int connection_exit_begin_conn(cell_t *cell, circuit_t *circ); int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ); -void connection_exit_connect(edge_connection_t *conn); +MOCK_DECL(void,connection_exit_connect,(edge_connection_t *conn)); int connection_edge_is_rendezvous_stream(const edge_connection_t *conn); int connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit); @@ -290,8 +290,7 @@ 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(edge_connection_t *edge_conn, - hs_circuit_id_protocol_t protocol); +STATIC int handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn); struct half_edge_t; STATIC void connection_half_edge_add(const edge_connection_t *conn, diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 480ff7739ad..d47ef5149c7 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -144,6 +144,12 @@ mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, return 0; } +static void +mock_connection_exit_connect(edge_connection_t *edge_conn) +{ + tor_assert(edge_conn); +} + static unsigned int num_intro_points = 0; static unsigned int mock_count_desc_circuit_established(const hs_service_descriptor_t *desc) @@ -2097,6 +2103,7 @@ static void test_export_client_circuit_id(void *arg) { origin_circuit_t *or_circ = NULL; + int retval; size_t sz; char *cp1=NULL, *cp2=NULL; connection_t *conn = NULL; @@ -2104,71 +2111,255 @@ test_export_client_circuit_id(void *arg) (void) arg; MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + MOCK(connection_exit_connect, mock_connection_exit_connect); hs_service_init(); /* Create service */ hs_service_t *service = helper_create_service(); - service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; - - /* 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; + /* Add HiddenServicePort directives */ + smartlist_add(service->config.ports, + hs_parse_port_config("8000 8000", " ", NULL)); + smartlist_add(service->config.ports, + hs_parse_port_config("8001 8001 none", " ", NULL)); + smartlist_add(service->config.ports, + hs_parse_port_config("8002 8002 haproxy", " ", NULL)); /* 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->base_.purpose = CIRCUIT_PURPOSE_S_CONNECT_REND; + or_circ->hs_ident = hs_ident_circuit_new(&service->keys.identity_pk); or_circ->global_identifier = 666; - /* Export circuit ID */ - export_hs_client_circuit_id(edge_conn, service->config.circuit_id_protocol); + /* Setup the circuit: do the ntor key exchange */ + uint8_t ntor_key_seed[DIGEST256_LEN] = {2}; + retval = hs_circuit_setup_e2e_rend_circ(or_circ, ntor_key_seed, + sizeof(ntor_key_seed), 1); + tt_int_op(retval, OP_EQ, 0); - /* 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"); + /* Has HiddenServiceExportCircuitID set to none. The export circuit id + * protocol will be determined only by HiddenServicePort directives. */ + { + service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_NONE; + + /* Unspecified protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8000; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); + + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, ""); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* None protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8001; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); + + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, ""); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* HAProxy protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_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 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + } - /* Change circ GID and see that the reported circuit ID also changes */ - or_circ->global_identifier = 22; + /* Has HiddenServiceExportCircuitID set to none. The export circuit id + * protocol will be determined only by HiddenServicePort directives. */ + { + service->config.circuit_id_protocol = HS_CIRCUIT_ID_PROTOCOL_HAPROXY; + + /* Unspecified protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8000; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_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 8000\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* None protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8001; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_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 8001\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* HAProxy protocol in HiddenServiceDir */ + { + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_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 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + } - /* check changes */ - 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); + /* Edge cases for circuit ids */ - /* Check that GID with UINT32_MAX works. */ - or_circ->global_identifier = UINT32_MAX; + /* Change circuit id */ + { + or_circ->global_identifier = 22; - 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"); - tor_free(cp1); + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; - /* Check that GID with UINT16_MAX works. */ - or_circ->global_identifier = UINT16_MAX; + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); - 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"); - tor_free(cp1); + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::0:16 ::1 22 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } - /* Check that GID with UINT16_MAX + 7 works. */ - or_circ->global_identifier = UINT16_MAX + 7; + /* Circuit id of UINT32_MAX */ + { + or_circ->global_identifier = UINT32_MAX; + + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); + + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::ffff:ffff ::1 65535 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* Circuit id of UINT16_MAX */ + { + or_circ->global_identifier = UINT16_MAX; + + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); - 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"); + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::0:ffff ::1 65535 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } + + /* Circuit id of UINT16_MAX + 7*/ + { + or_circ->global_identifier = UINT16_MAX + 7; + + conn = test_conn_get_connection(EXIT_CONN_STATE_OPEN, CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + edge_connection_t *edge_conn = TO_EDGE_CONN(conn); + /* Connect to the virtual port 8000 */ + edge_conn->base_.port = 8002; + + handle_hs_exit_conn(TO_CIRCUIT(or_circ), edge_conn); + + /* Check contents */ + cp1 = buf_get_contents(conn->outbuf, &sz); + tt_str_op(cp1, OP_EQ, + "PROXY TCP6 fc00:dead:beef:4dad::1:6 ::1 6 8002\r\n"); + + connection_free_minimal(conn); + conn = NULL; + tor_free(cp1); + } done: UNMOCK(connection_write_to_buf_impl_); + UNMOCK(connection_exit_connect); circuit_free_(TO_CIRCUIT(or_circ)); connection_free_minimal(conn); hs_service_free(service);