瀏覽代碼

Discard circuit paths on which nobody supports ntor

Right now this accounts for about 1% of circuits over all, but if you
pick a guard that's running 0.2.3, it will be about 6% of the circuits
running through that guard.

Making sure that every circuit has at least one ntor link means that
we're getting plausibly good forward secrecy on every circuit.

This implements ticket 9777,
Nick Mathewson 10 年之前
父節點
當前提交
1068e50aec
共有 4 個文件被更改,包括 81 次插入10 次删除
  1. 3 0
      changes/feature9777
  2. 68 9
      src/or/circuitbuild.c
  3. 9 1
      src/or/circuitlist.c
  4. 1 0
      src/or/circuitlist.h

+ 3 - 0
changes/feature9777

@@ -0,0 +1,3 @@
+  o Minor features:
+    - Avoid using circuit paths if no node in the path supports the ntor
+      circuit extension handshake. Implements ticket 9777.

+ 68 - 9
src/or/circuitbuild.c

@@ -72,6 +72,9 @@ static void pathbias_count_use_failed(origin_circuit_t *circ);
 static void pathbias_measure_use_rate(entry_guard_t *guard);
 static void pathbias_measure_use_rate(entry_guard_t *guard);
 static void pathbias_measure_close_rate(entry_guard_t *guard);
 static void pathbias_measure_close_rate(entry_guard_t *guard);
 static void pathbias_scale_use_rates(entry_guard_t *guard);
 static void pathbias_scale_use_rates(entry_guard_t *guard);
+#ifdef CURVE25519_ENABLED
+static int circuits_can_use_ntor(void);
+#endif
 
 
 /** This function tries to get a channel to the specified endpoint,
 /** This function tries to get a channel to the specified endpoint,
  * and then calls command_setup_channel() to give it the right
  * and then calls command_setup_channel() to give it the right
@@ -284,21 +287,74 @@ circuit_rep_hist_note_result(origin_circuit_t *circ)
   } while (hop!=circ->cpath);
   } while (hop!=circ->cpath);
 }
 }
 
 
+#ifdef CURVE25519_ENABLED
+/** Return 1 iff at least one node in circ's cpath supports ntor. */
+static int
+circuit_cpath_supports_ntor(const origin_circuit_t *circ)
+{
+  crypt_path_t *head = circ->cpath, *cpath = circ->cpath;
+
+  cpath = head;
+  do {
+    if (cpath->extend_info &&
+        !tor_mem_is_zero(
+            (const char*)cpath->extend_info->curve25519_onion_key.public_key,
+            CURVE25519_PUBKEY_LEN))
+      return 1;
+
+    cpath = cpath->next;
+  } while (cpath != head);
+
+  return 0;
+}
+#else
+#define circuit_cpath_supports_ntor(circ) 0
+#endif
+
 /** Pick all the entries in our cpath. Stop and return 0 when we're
 /** Pick all the entries in our cpath. Stop and return 0 when we're
  * happy, or return -1 if an error occurs. */
  * happy, or return -1 if an error occurs. */
 static int
 static int
 onion_populate_cpath(origin_circuit_t *circ)
 onion_populate_cpath(origin_circuit_t *circ)
 {
 {
-  int r;
- again:
-  r = onion_extend_cpath(circ);
-  if (r < 0) {
-    log_info(LD_CIRC,"Generating cpath hop failed.");
-    return -1;
+  int n_tries = 0;
+#ifdef CURVE25519_ENABLED
+  const int using_ntor = circuits_can_use_ntor();
+#else
+  const int using_ntor = 0;
+#endif
+
+#define MAX_POPULATE_ATTEMPTS 32
+
+  while (1) {
+    int r = onion_extend_cpath(circ);
+    if (r < 0) {
+      log_info(LD_CIRC,"Generating cpath hop failed.");
+      return -1;
+    }
+    if (r == 1) {
+      /* This circuit doesn't need/shouldn't be forced to have an ntor hop */
+      if (circ->build_state->desired_path_len <= 1 || ! using_ntor)
+        return 0;
+
+      /* This circuit has an ntor hop. great! */
+      if (circuit_cpath_supports_ntor(circ))
+        return 0;
+
+      /* No node in the circuit supports ntor.  Have we already tried too many
+       * times? */
+      if (++n_tries >= MAX_POPULATE_ATTEMPTS)
+        break;
+
+      /* Clear the path and retry */
+      circuit_clear_cpath(circ);
+    }
   }
   }
-  if (r == 0)
-    goto again;
-  return 0; /* if r == 1 */
+  log_warn(LD_CIRC, "I tried for %d times, but I couldn't build a %d-hop "
+           "circuit with at least one node that supports ntor.",
+           MAX_POPULATE_ATTEMPTS,
+           circ->build_state->desired_path_len);
+
+  return -1;
 }
 }
 
 
 /** Create and return a new origin circuit. Initialize its purpose and
 /** Create and return a new origin circuit. Initialize its purpose and
@@ -3475,6 +3531,9 @@ onion_next_hop_in_cpath(crypt_path_t *cpath)
 
 
 /** Choose a suitable next hop in the cpath <b>head_ptr</b>,
 /** Choose a suitable next hop in the cpath <b>head_ptr</b>,
  * based on <b>state</b>. Append the hop info to head_ptr.
  * based on <b>state</b>. Append the hop info to head_ptr.
+ *
+ * Return 1 if the path is complete, 0 if we successfully added a hop,
+ * and -1 on error.
  */
  */
 static int
 static int
 onion_extend_cpath(origin_circuit_t *circ)
 onion_extend_cpath(origin_circuit_t *circ)

+ 9 - 1
src/or/circuitlist.c

@@ -709,7 +709,7 @@ circuit_free_cpath(crypt_path_t *cpath)
   if (!cpath)
   if (!cpath)
     return;
     return;
 
 
-  /* it's a doubly linked list, so we have to notice when we've
+  /* it's a circular list, so we have to notice when we've
    * gone through it once. */
    * gone through it once. */
   while (cpath->next && cpath->next != head) {
   while (cpath->next && cpath->next != head) {
     victim = cpath;
     victim = cpath;
@@ -720,6 +720,14 @@ circuit_free_cpath(crypt_path_t *cpath)
   circuit_free_cpath_node(cpath);
   circuit_free_cpath_node(cpath);
 }
 }
 
 
+/** Remove all the items in the cpath on <b>circ</b>.*/
+void
+circuit_clear_cpath(origin_circuit_t *circ)
+{
+  circuit_free_cpath(circ->cpath);
+  circ->cpath = NULL;
+}
+
 /** Release all storage held by circuits. */
 /** Release all storage held by circuits. */
 void
 void
 circuit_free_all(void)
 circuit_free_all(void)

+ 1 - 0
src/or/circuitlist.h

@@ -50,6 +50,7 @@ void circuit_mark_all_dirty_circs_as_unusable(void);
 void circuit_mark_for_close_(circuit_t *circ, int reason,
 void circuit_mark_for_close_(circuit_t *circ, int reason,
                              int line, const char *file);
                              int line, const char *file);
 int circuit_get_cpath_len(origin_circuit_t *circ);
 int circuit_get_cpath_len(origin_circuit_t *circ);
+void circuit_clear_cpath(origin_circuit_t *circ);
 crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum);
 crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum);
 void circuit_get_all_pending_on_channel(smartlist_t *out,
 void circuit_get_all_pending_on_channel(smartlist_t *out,
                                         channel_t *chan);
                                         channel_t *chan);