aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/or/address_set.c74
-rw-r--r--src/core/or/address_set.h18
-rw-r--r--src/core/or/connection_edge.c25
3 files changed, 116 insertions, 1 deletions
diff --git a/src/core/or/address_set.c b/src/core/or/address_set.c
index d618ffc3d6..fcddc55e9f 100644
--- a/src/core/or/address_set.c
+++ b/src/core/or/address_set.c
@@ -69,3 +69,77 @@ address_set_probably_contains(const address_set_t *set,
{
return bloomfilt_probably_contains(set, addr);
}
+
+/* Length of the item is an address (IPv4 or IPv6) and a 2 byte port. We use
+ * 16 bytes for the address here (IPv6) since we do not know which family
+ * the given address in the item thus in the case of IPv4, the extra bytes
+ * are simply zeroes to accomodate. */
+#define BLOOMFILT_ADDR_PORT_ITEM_LEN (16 + sizeof(uint16_t))
+
+/** Build an item for the bloomfilter consisting of an address and port pair.
+ *
+ * If the given address is _not_ AF_INET or AF_INET6, then the item is an
+ * array of 0s.
+ *
+ * Return a pointer to a static buffer containing the item. Next call to this
+ * function invalidates its previous content. */
+static const uint8_t *
+build_addr_port_item(const tor_addr_t *addr, const uint16_t port)
+{
+ static uint8_t data[BLOOMFILT_ADDR_PORT_ITEM_LEN];
+
+ memset(data, 0, sizeof(data));
+ switch (tor_addr_family(addr)) {
+ case AF_INET:
+ memcpy(data, &addr->addr.in_addr.s_addr, 4);
+ break;
+ case AF_INET6:
+ memcpy(data, &addr->addr.in6_addr.s6_addr, 16);
+ break;
+ case AF_UNSPEC:
+ /* Leave the 0. */
+ break;
+ default:
+ /* LCOV_EXCL_START */
+ tor_fragile_assert();
+ /* LCOV_EXCL_STOP */
+ }
+
+ memcpy(data + 16, &port, sizeof(port));
+ return data;
+}
+
+/** Return a hash value for the given item that the bloomfilter will use. */
+static uint64_t
+bloomfilt_addr_port_hash(const struct sipkey *key,
+ const void *item)
+{
+ return siphash24(item, BLOOMFILT_ADDR_PORT_ITEM_LEN, key);
+}
+
+/** Allocate and return an addr_port_set_t, suitable for holding up to
+ * max_address_guess distinct values. */
+addr_port_set_t *
+addr_port_set_new(int max_addresses_guess)
+{
+ uint8_t k[BLOOMFILT_KEY_LEN];
+ crypto_rand((void*)k, sizeof(k));
+ return bloomfilt_new(max_addresses_guess, bloomfilt_addr_port_hash, k);
+}
+
+/** Add an address and port pair to the given set. */
+void
+addr_port_set_add(addr_port_set_t *set, const tor_addr_t *addr, uint16_t port)
+{
+ bloomfilt_add(set, build_addr_port_item(addr, port));
+}
+
+/** Return true if the given address and port pair are in the set. Of course,
+ * this is a bloomfilter and thus in rare occasion, a false positive happens
+ * thus the "probably". */
+bool
+addr_port_set_probably_contains(const addr_port_set_t *set,
+ const tor_addr_t *addr, uint16_t port)
+{
+ return !!bloomfilt_probably_contains(set, build_addr_port_item(addr, port));
+}
diff --git a/src/core/or/address_set.h b/src/core/or/address_set.h
index 33887a7c9e..2c78ab0576 100644
--- a/src/core/or/address_set.h
+++ b/src/core/or/address_set.h
@@ -13,13 +13,14 @@
#include "lib/cc/torint.h"
#include "lib/container/bloomfilt.h"
+struct tor_addr_t;
+
/**
* An address_set_t represents a set of tor_addr_t values. The implementation
* is probabilistic: false negatives cannot occur but false positives are
* possible.
*/
typedef struct bloomfilt_t address_set_t;
-struct tor_addr_t;
address_set_t *address_set_new(int max_addresses_guess);
#define address_set_free(set) bloomfilt_free(set)
@@ -28,4 +29,19 @@ void address_set_add_ipv4h(address_set_t *set, uint32_t addr);
int address_set_probably_contains(const address_set_t *set,
const struct tor_addr_t *addr);
+/**
+ * An addr_port_set_t represents a set of tor_addr_t values with a uint16_t
+ * port value. The implementation is probabilistic: false negatives cannot
+ * occur but false positives are possible.
+ */
+typedef struct bloomfilt_t addr_port_set_t;
+
+addr_port_set_t *addr_port_set_new(int max_addresses_guess);
+#define addr_port_set_free(s) bloomfilt_free(s)
+void addr_port_set_add(addr_port_set_t *set,
+ const struct tor_addr_t *addr, uint16_t port);
+bool addr_port_set_probably_contains(const addr_port_set_t *set,
+ const struct tor_addr_t *addr,
+ uint16_t port);
+
#endif /* !defined(TOR_ADDRESS_SET_H) */
diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c
index 37e83ba71e..80b02ccad3 100644
--- a/src/core/or/connection_edge.c
+++ b/src/core/or/connection_edge.c
@@ -4181,6 +4181,31 @@ connection_exit_connect(edge_connection_t *edge_conn)
return;
}
+ /* Next, check for attempts to connect back into the Tor network. We don't
+ * want to allow these for the same reason we don't want to allow
+ * infinite-length circuits (see "A Practical Congestion Attack on Tor Using
+ * Long Paths", Usenix Security 2009). See also ticket 2667.
+ *
+ * The TORPROTOCOL reason is used instead of EXITPOLICY so client do NOT
+ * attempt to retry connecting onto another circuit that will also fail
+ * bringing considerable more load on the network if so.
+ *
+ * Since the address+port set here is a bloomfilter, in very rare cases, the
+ * check will create a false positive meaning that the destination could
+ * actually be legit and thus being denied exit. However, sending back a
+ * reason that makes the client retry results in much worst consequences in
+ * case of an attack so this is a small price to pay. */
+ if (!connection_edge_is_rendezvous_stream(edge_conn) &&
+ nodelist_reentry_probably_contains(&conn->addr, conn->port)) {
+ log_info(LD_EXIT, "%s:%d tried to connect back to a known relay address. "
+ "Closing.", escaped_safe_str_client(conn->address),
+ conn->port);
+ connection_edge_end(edge_conn, END_STREAM_REASON_TORPROTOCOL);
+ circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn);
+ connection_free(conn);
+ return;
+ }
+
#ifdef HAVE_SYS_UN_H
if (conn->socket_family != AF_UNIX) {
#else