Bläddra i källkod

Implement stream isolation

This is the meat of proposal 171: we change circuit_is_acceptable()
to require that the connection is compatible with every connection
that has been linked to the circuit; we update circuit_is_better to
prefer attaching streams to circuits in the way that decreases the
circuits' usefulness the least; and we update link_apconn_to_circ()
to do the appropriate bookkeeping.
Nick Mathewson 13 år sedan
förälder
incheckning
773bfaf91e
4 ändrade filer med 75 tillägg och 3 borttagningar
  1. 11 0
      changes/prop171
  2. 26 0
      src/common/util.c
  3. 1 0
      src/common/util.h
  4. 37 3
      src/or/circuituse.c

+ 11 - 0
changes/prop171

@@ -1,3 +1,14 @@
+  o Major features:
+    - You can now configure Tor so that streams from different
+      applications are isolated on different circuits, to prevent an
+      attacker who sees your streams leaving an exit node from linking
+      your sessions to one another.  To do this, choose some way to
+      distinguish the applications -- have them connect to different
+      SocksPorts, or have one of them use SOCKS4 while the other uses
+      SOCKS5, or have them pass different authentication strings to
+      the SOCKS proxy.  Then use the new SocksPort syntax to configure
+      the degree of isolation you need. This implements Proposal 171.
+
   o Minor features:
     - There's a new syntax for specifying multiple client ports (such as
       SOCKSPort, TransPort, DNSPort, NATDPort): you can now just declare

+ 26 - 0
src/common/util.c

@@ -412,6 +412,32 @@ round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor)
   return number;
 }
 
+/** Return the number of bits set in <b>v</b>. */
+int
+n_bits_set_u8(uint8_t v)
+{
+  static const int nybble_table[] = {
+    0, /* 0000 */
+    1, /* 0001 */
+    1, /* 0010 */
+    2, /* 0011 */
+    1, /* 0100 */
+    2, /* 0101 */
+    2, /* 0110 */
+    3, /* 0111 */
+    1, /* 1000 */
+    2, /* 1001 */
+    2, /* 1010 */
+    3, /* 1011 */
+    2, /* 1100 */
+    3, /* 1101 */
+    3, /* 1110 */
+    4, /* 1111 */
+  };
+
+  return nybble_table[v & 15] + nybble_table[v>>4];
+}
+
 /* =====
  * String manipulation
  * ===== */

+ 1 - 0
src/common/util.h

@@ -160,6 +160,7 @@ uint64_t round_to_power_of_2(uint64_t u64);
 unsigned round_to_next_multiple_of(unsigned number, unsigned divisor);
 uint32_t round_uint32_to_next_multiple_of(uint32_t number, uint32_t divisor);
 uint64_t round_uint64_to_next_multiple_of(uint64_t number, uint64_t divisor);
+int n_bits_set_u8(uint8_t v);
 
 /* Compute the CEIL of <b>a</b> divided by <b>b</b>, for nonnegative <b>a</b>
  * and positive <b>b</b>.  Works on integer types only. Not defined if a+b can

+ 37 - 3
src/or/circuituse.c

@@ -143,18 +143,27 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ,
       return 0;
     }
   }
+
+  if (!connection_edge_compatible_with_circuit(conn, origin_circ)) {
+    /* conn needs to be isolated from other conns that have already used
+     * origin_circ */
+    return 0;
+  }
+
   return 1;
 }
 
 /** Return 1 if circuit <b>a</b> is better than circuit <b>b</b> for
- * <b>purpose</b>, and return 0 otherwise. Used by circuit_get_best.
+ * <b>conn</b>, and return 0 otherwise. Used by circuit_get_best.
  */
 static int
 circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
-                  uint8_t purpose)
+                  const edge_connection_t *conn)
 {
   const circuit_t *a = TO_CIRCUIT(oa);
   const circuit_t *b = TO_CIRCUIT(ob);
+  const uint8_t purpose = conn->_base.purpose;
+  int a_bits, b_bits;
 
   switch (purpose) {
     case CIRCUIT_PURPOSE_C_GENERAL:
@@ -188,6 +197,29 @@ circuit_is_better(const origin_circuit_t *oa, const origin_circuit_t *ob,
         return 1;
       break;
   }
+
+  /* XXXX023 Maybe this check should get a higher priority to avoid
+   *   using up circuits too rapidly. */
+
+  a_bits = connection_edge_update_circuit_isolation(conn,
+                                                    (origin_circuit_t*)oa, 1);
+  b_bits = connection_edge_update_circuit_isolation(conn,
+                                                    (origin_circuit_t*)ob, 1);
+  /* if x_bits < 0, then we have not used x for anything; better not to dirty
+   * a connection if we can help it. */
+  if (a_bits < 0) {
+    return 0;
+  } else if (b_bits < 0) {
+    return 1;
+  }
+  a_bits &= ~ oa->isolation_flags_mixed;
+  a_bits &= ~ ob->isolation_flags_mixed;
+  if (n_bits_set_u8(a_bits) < n_bits_set_u8(b_bits)) {
+    /* The fewer new restrictions we need to make on a circuit for stream
+     * isolation, the better. */
+    return 1;
+  }
+
   return 0;
 }
 
@@ -244,7 +276,7 @@ circuit_get_best(const edge_connection_t *conn,
     /* now this is an acceptable circ to hand back. but that doesn't
      * mean it's the *best* circ to hand back. try to decide.
      */
-    if (!best || circuit_is_better(origin_circ,best,purpose))
+    if (!best || circuit_is_better(origin_circ,best,conn))
       best = origin_circ;
   }
 
@@ -1476,6 +1508,8 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
     tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
     apconn->cpath_layer = circ->cpath->prev;
   }
+
+  connection_edge_update_circuit_isolation(apconn, circ, 0);
 }
 
 /** Return true iff <b>address</b> is matched by one of the entries in