]> git.ipfire.org Git - thirdparty/tor.git/commitdiff
client-side support for negotiating CGO via subprotocol request
authorNick Mathewson <nickm@torproject.org>
Mon, 2 Jun 2025 19:59:01 +0000 (15:59 -0400)
committerNick Mathewson <nickm@torproject.org>
Tue, 10 Jun 2025 23:06:47 +0000 (19:06 -0400)
This has been a bit tricky, since the old code assumed that
we never wanted to use extensions besides CC.

src/core/crypto/onion_crypto.c
src/core/or/circuitbuild.c
src/core/or/circuitbuild.h
src/core/or/congestion_control_common.c
src/core/or/congestion_control_common.h
src/core/or/extend_info_st.h

index edd35af91d0eb25de3e4909a76d1e14624b24d49..8a41fe848765e7f69e3d9185d6e33bd0cff591f8 100644 (file)
@@ -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;
index ee8715a745d3b6ddd717f628f223793ace4ae79b..26ff519f6d1927279353c534109840f7736373b2 100644 (file)
@@ -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;
 }
index 6b49de53cf78d3fba2e33f98ac1c6891ef58da79..60d718bd16ac32898b62f9a78d107fd1e5c68130 100644 (file)
@@ -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);
index 734d5142efa1d82da68ff68739c712f17a5ee518..eb7564f00096da428bc9315c1ad71946e5bc61e2 100644 (file)
@@ -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;
 }
 
 /**
index c4bc02e9089ae302f7c8a67db4d920d3a0168117..530763b21310c750598b0023f7233a6fe035717f 100644 (file)
@@ -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,
index 5f59bd2299c8b1e1d8e69839a17eec04a482b1f4..71fa260d7592aae9a11462187b1ecf9dbc44dc59 100644 (file)
@@ -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) */