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.
This commit is contained in:
Nick Mathewson 2011-07-06 17:08:24 -04:00
parent 1d3c8c1f74
commit 773bfaf91e
4 changed files with 75 additions and 3 deletions

View File

@ -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

View File

@ -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
* ===== */

View File

@ -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

View File

@ -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