Commit 773bfaf9 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

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.
parent 1d3c8c1f
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
  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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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