From: Nick Mathewson Date: Mon, 2 Jun 2025 19:59:01 +0000 (-0400) Subject: client-side support for negotiating CGO via subprotocol request X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9f2f359494f826f9fe57573b46daefe30b493dc1;p=thirdparty%2Ftor.git client-side support for negotiating CGO via subprotocol request This has been a bit tricky, since the old code assumed that we never wanted to use extensions besides CC. --- diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c index edd35af91d..8a41fe8487 100644 --- a/src/core/crypto/onion_crypto.c +++ b/src/core/crypto/onion_crypto.c @@ -182,7 +182,8 @@ onion_skin_create(int type, return -1; size_t msg_len = 0; uint8_t *msg = NULL; - if (client_circ_negotiation_message(node, &msg, &msg_len) < 0) + if (client_circ_negotiation_message(node, &msg, &msg_len, + &state_out->chosen_params) < 0) return -1; uint8_t *onion_skin = NULL; size_t onion_skin_len = 0; diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index ee8715a745..26ff519f6d 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -52,6 +52,7 @@ #include "core/or/relay.h" #include "core/or/trace_probes_circuit.h" #include "core/or/crypt_path.h" +#include "core/or/protover.h" #include "feature/client/bridges.h" #include "feature/client/circpathbias.h" #include "feature/client/entrynodes.h" @@ -84,6 +85,7 @@ #include "trunnel/extension.h" #include "trunnel/congestion_control.h" +#include "trunnel/subproto_request.h" static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); @@ -883,6 +885,9 @@ circuit_pick_create_handshake(uint8_t *cell_type_out, if (ei->exit_supports_congestion_control && congestion_control_enabled()) *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3; + else if (ei->enable_cgo) + // XXXX CGO: provide an enable option? + *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR_V3; else *handshake_type_out = ONION_HANDSHAKE_TYPE_NTOR; } @@ -2636,6 +2641,80 @@ circuit_upgrade_circuits_from_guard_wait(void) smartlist_free(to_upgrade); } +// TODO: Find a better place to declare this; it's duplicated in +// onion_crypto.c +#define EXT_TYPE_SUBPROTO 3 + +/** Add a request for the CGO subprotocol capability to ext. + * + * NOTE: If we need to support other subprotocol extensions, + * do not add separate functions! Instead rename this function + * and adapt it as appropriate. + */ +static int +build_cgo_subproto_request(trn_extension_t *ext) +{ + trn_extension_field_t *fld = NULL; + trn_subproto_request_t *req = NULL; + trn_subproto_request_ext_t *req_ext = NULL; + int r = 0; + + fld = trn_extension_field_new(); + req_ext = trn_subproto_request_ext_new(); + + req = trn_subproto_request_new(); + req->protocol_id = PRT_RELAY; + req->proto_cap_number = PROTOVER_RELAY_CRYPT_CGO; + trn_subproto_request_ext_add_reqs(req_ext, req); + req = NULL; // prevent double-free + + // TODO: If we add other capabilities here, we need to make + // sure they are correctly sorted. + + ssize_t len = trn_subproto_request_ext_encoded_len(req_ext); + if (BUG(len<0)) + goto err; + if (BUG(len > UINT8_MAX)) + goto err; + + trn_extension_field_setlen_field(fld, len); + trn_extension_field_set_field_type(fld, EXT_TYPE_SUBPROTO); + trn_extension_field_set_field_len(fld, len); + uint8_t *out = trn_extension_field_getarray_field(fld); + ssize_t len2 = trn_subproto_request_ext_encode(out, len, req_ext); + if (BUG(len != len2)) + goto err; + + trn_extension_add_fields(ext, fld); + fld = NULL; // prevent double-free + + // We succeeded! + r = 0; + + err: + trn_subproto_request_ext_free(req_ext); + trn_subproto_request_free(req); + trn_extension_field_free(fld); + + return r; +} + +/** Helper: Comparison function to sort extensions. */ +static int +ext_cmp(const void *a, const void *b) +{ + const trn_extension_field_t *fa = *(trn_extension_field_t **)a; + const trn_extension_field_t *fb = *(trn_extension_field_t **)b; + uint8_t ta = trn_extension_field_get_field_type(fa); + uint8_t tb = trn_extension_field_get_field_type(fb); + if (ta < tb) + return -1; + else if (ta == tb) + return 0; + else + return 1; +} + /** * Try to generate a circuit-negotiation message for communication with a * given relay. Assumes we are using ntor v3, or some later version that @@ -2647,13 +2726,53 @@ circuit_upgrade_circuits_from_guard_wait(void) int client_circ_negotiation_message(const extend_info_t *ei, uint8_t **msg_out, - size_t *msg_len_out) + size_t *msg_len_out, + circuit_params_t *params_out) { - tor_assert(ei && msg_out && msg_len_out); + tor_assert(ei && msg_out && msg_len_out && params_out); + bool cc_enabled = false; - if (!ei->exit_supports_congestion_control) { - return -1; + *msg_out = NULL; + + trn_extension_t *ext = trn_extension_new(); + + if (ei->exit_supports_congestion_control && + congestion_control_enabled()) { + if (congestion_control_build_ext_request(ext) < 0) { + goto err; + } + cc_enabled = true; } - return congestion_control_build_ext_request(msg_out, msg_len_out); + if (cc_enabled && ei->enable_cgo) { + if (build_cgo_subproto_request(ext) < 0) { + goto err; + } + params_out->cell_fmt = RELAY_CELL_FORMAT_V1; + params_out->crypto_alg = RELAY_CRYPTO_ALG_CGO_CLIENT; + } + + size_t n_fields = trn_extension_getlen_fields(ext); + qsort(trn_extension_getarray_fields(ext), + n_fields, sizeof(trn_extension_field_t *), + ext_cmp); + + trn_extension_set_num(ext, n_fields); + + ssize_t total_len = trn_extension_encoded_len(ext); + if (BUG(total_len < 0)) + goto err; + + *msg_out = tor_malloc_zero(total_len); + *msg_len_out = total_len; + if (BUG(trn_extension_encode(*msg_out, total_len, ext) < 0)) { + goto err; + } + trn_extension_free(ext); + + return 0; + err: + trn_extension_free(ext); + tor_free(*msg_out); + return -1; } diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h index 6b49de53cf..60d718bd16 100644 --- a/src/core/or/circuitbuild.h +++ b/src/core/or/circuitbuild.h @@ -70,9 +70,11 @@ circuit_deliver_create_cell,(circuit_t *circ, const struct create_cell_t *create_cell, int relayed)); +struct circuit_params_t; int client_circ_negotiation_message(const extend_info_t *ei, uint8_t **msg_out, - size_t *msg_len_out); + size_t *msg_len_out, + struct circuit_params_t *params_out); #ifdef CIRCUITBUILD_PRIVATE STATIC circid_t get_unique_circ_id_by_chan(channel_t *chan); diff --git a/src/core/or/congestion_control_common.c b/src/core/or/congestion_control_common.c index 734d5142ef..eb7564f000 100644 --- a/src/core/or/congestion_control_common.c +++ b/src/core/or/congestion_control_common.c @@ -998,26 +998,18 @@ congestion_control_dispatch_cc_alg(congestion_control_t *cc, * Build an extension field request to negotiate congestion control. * * If congestion control is enabled, field TRUNNEL_EXT_TYPE_CC_FIELD_REQUEST - * is created in msg_out. It is a single 0-length field that signifies that we - * want to use congestion control. The length of msg_out is provided via - * msg_len_out. + * added to ext. It is a single 0-length field that signifies that we + * want to use congestion control. * - * If congestion control is not enabled, a payload with 0 extensions is created - * and returned. + * If congestion control is not enabled, no extension is added. * * If there is a failure building the request, -1 is returned, else 0. - * - * *msg_out must be freed if the return value is 0. */ int -congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out) +congestion_control_build_ext_request(trn_extension_t *ext) { - uint8_t *request = NULL; - trn_extension_t *ext = NULL; trn_extension_field_t *field = NULL; - ext = trn_extension_new(); - /* With congestion control enabled, add the request, else it is an empty * request in the payload. */ @@ -1032,30 +1024,9 @@ congestion_control_build_ext_request(uint8_t **msg_out, size_t *msg_len_out) /* Build final extension. */ trn_extension_add_fields(ext, field); - trn_extension_set_num(ext, 1); - } - - /* Encode extension. */ - ssize_t ret = trn_extension_encoded_len(ext); - if (BUG(ret < 0)) { - goto err; - } - size_t request_len = ret; - request = tor_malloc_zero(request_len); - ret = trn_extension_encode(request, request_len, ext); - if (BUG(ret < 0)) { - tor_free(request); - goto err; } - *msg_out = request; - *msg_len_out = request_len; - - /* Free everything, we've encoded the request now. */ - ret = 0; - err: - trn_extension_free(ext); - return (int)ret; + return 0; } /** diff --git a/src/core/or/congestion_control_common.h b/src/core/or/congestion_control_common.h index c4bc02e908..530763b213 100644 --- a/src/core/or/congestion_control_common.h +++ b/src/core/or/congestion_control_common.h @@ -66,9 +66,9 @@ void congestion_control_new_consensus_params(const networkstatus_t *ns); bool congestion_control_enabled(void); -int congestion_control_build_ext_request(uint8_t **msg_out, - size_t *msg_len_out); struct trn_extension_st; +int congestion_control_build_ext_request(struct trn_extension_st *ext); + int congestion_control_parse_ext_request(const struct trn_extension_st *ext); int congestion_control_build_ext_response(const circuit_params_t *our_params, const circuit_params_t *circ_params, diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h index 5f59bd2299..71fa260d75 100644 --- a/src/core/or/extend_info_st.h +++ b/src/core/or/extend_info_st.h @@ -40,6 +40,11 @@ struct extend_info_t { * and it also supports supports NtorV3 _and_ negotiation * of congestion control parameters */ bool exit_supports_congestion_control; + /** + * True if this hop supports CGO relay message enryption, + * and we intend to use it. + */ + bool enable_cgo; }; #endif /* !defined(EXTEND_INFO_ST_H) */