aboutsummaryrefslogtreecommitdiff
path: root/src/or/connection_edge.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/connection_edge.c')
-rw-r--r--src/or/connection_edge.c538
1 files changed, 405 insertions, 133 deletions
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index d210f93fa1..2a1a2f0fd2 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -1,7 +1,7 @@
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
- * Copyright (c) 2007-2013, The Tor Project, Inc. */
+ * Copyright (c) 2007-2015, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
@@ -46,6 +46,19 @@
#ifdef HAVE_LINUX_NETFILTER_IPV4_H
#include <linux/netfilter_ipv4.h>
#define TRANS_NETFILTER
+#define TRANS_NETFILTER_IPV4
+#endif
+
+#ifdef HAVE_LINUX_IF_H
+#include <linux/if.h>
+#endif
+
+#ifdef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#if defined(IP6T_SO_ORIGINAL_DST)
+#define TRANS_NETFILTER
+#define TRANS_NETFILTER_IPV6
+#endif
#endif
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
@@ -54,6 +67,10 @@
#define TRANS_PF
#endif
+#ifdef IP_TRANSPARENT
+#define TRANS_TPROXY
+#endif
+
#define SOCKS4_GRANTED 90
#define SOCKS4_REJECT 91
@@ -744,8 +761,9 @@ connection_ap_fail_onehop(const char *failed_digest,
/* we don't know the digest; have to compare addr:port */
tor_addr_t addr;
if (!build_state || !build_state->chosen_exit ||
- !entry_conn->socks_request)
+ !entry_conn->socks_request) {
continue;
+ }
if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 ||
!tor_addr_eq(&build_state->chosen_exit->addr, &addr) ||
build_state->chosen_exit->port != entry_conn->socks_request->port)
@@ -894,78 +912,102 @@ connection_ap_rewrite_and_attach_if_allowed(entry_connection_t *conn,
return connection_ap_handshake_rewrite_and_attach(conn, circ, cpath);
}
-/** Connection <b>conn</b> just finished its socks handshake, or the
- * controller asked us to take care of it. If <b>circ</b> is defined,
- * then that's where we'll want to attach it. Otherwise we have to
- * figure it out ourselves.
- *
- * First, parse whether it's a .exit address, remap it, and so on. Then
- * if it's for a general circuit, try to attach it to a circuit (or launch
- * one as needed), else if it's for a rendezvous circuit, fetch a
- * rendezvous descriptor first (or attach/launch a circuit if the
- * rendezvous descriptor is already here and fresh enough).
- *
- * The stream will exit from the hop
- * indicated by <b>cpath</b>, or from the last hop in circ's cpath if
- * <b>cpath</b> is NULL.
+/* Try to perform any map-based rewriting of the target address in
+ * <b>conn</b>, filling in the fields of <b>out</b> as we go, and modifying
+ * conn->socks_request.address as appropriate.
*/
-int
-connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
- origin_circuit_t *circ,
- crypt_path_t *cpath)
+STATIC void
+connection_ap_handshake_rewrite(entry_connection_t *conn,
+ rewrite_result_t *out)
{
socks_request_t *socks = conn->socks_request;
- hostname_type_t addresstype;
const or_options_t *options = get_options();
tor_addr_t addr_tmp;
- /* We set this to true if this is an address we should automatically
- * remap to a local address in VirtualAddrNetwork */
- int automap = 0;
- char orig_address[MAX_SOCKS_ADDR_LEN];
- time_t map_expires = TIME_MAX;
- time_t now = time(NULL);
- connection_t *base_conn = ENTRY_TO_CONN(conn);
- addressmap_entry_source_t exit_source = ADDRMAPSRC_NONE;
- tor_strlower(socks->address); /* normalize it */
- strlcpy(orig_address, socks->address, sizeof(orig_address));
+ /* Initialize all the fields of 'out' to reasonable defaults */
+ out->automap = 0;
+ out->exit_source = ADDRMAPSRC_NONE;
+ out->map_expires = TIME_MAX;
+ out->end_reason = 0;
+ out->should_close = 0;
+ out->orig_address[0] = 0;
+
+ /* We convert all incoming addresses to lowercase. */
+ tor_strlower(socks->address);
+ /* Remember the original address. */
+ strlcpy(out->orig_address, socks->address, sizeof(out->orig_address));
log_debug(LD_APP,"Client asked for %s:%d",
safe_str_client(socks->address),
socks->port);
+ /* Check for whether this is a .exit address. By default, those are
+ * disallowed when they're coming straight from the client, but you're
+ * allowed to have them in MapAddress commands and so forth. */
if (!strcmpend(socks->address, ".exit") && !options->AllowDotExit) {
log_warn(LD_APP, "The \".exit\" notation is disabled in Tor due to "
"security risks. Set AllowDotExit in your torrc to enable "
"it (at your own risk).");
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
+ out->end_reason = END_STREAM_REASON_TORPROTOCOL;
+ out->should_close = 1;
+ return;
}
- if (! conn->original_dest_address)
+ /* Remember the original address so we can tell the user about what
+ * they actually said, not just what it turned into. */
+ if (! conn->original_dest_address) {
+ /* Is the 'if' necessary here? XXXX */
conn->original_dest_address = tor_strdup(conn->socks_request->address);
+ }
+ /* First, apply MapAddress and MAPADDRESS mappings. We need to do
+ * these only for non-reverse lookups, since they don't exist for those.
+ * We need to do this before we consider automapping, since we might
+ * e.g. resolve irc.oftc.net into irconionaddress.onion, at which point
+ * we'd need to automap it. */
+ if (socks->command != SOCKS_COMMAND_RESOLVE_PTR) {
+ const unsigned rewrite_flags = AMR_FLAG_USE_MAPADDRESS;
+ if (addressmap_rewrite(socks->address, sizeof(socks->address),
+ rewrite_flags, &out->map_expires, &out->exit_source)) {
+ control_event_stream_status(conn, STREAM_EVENT_REMAP,
+ REMAP_STREAM_SOURCE_CACHE);
+ }
+ }
+
+ /* Now, handle automapping. Automapping happens when we're asked to
+ * resolve a hostname, and AutomapHostsOnResolve is set, and
+ * the hostname has a suffix listed in AutomapHostsSuffixes.
+ */
if (socks->command == SOCKS_COMMAND_RESOLVE &&
tor_addr_parse(&addr_tmp, socks->address)<0 &&
options->AutomapHostsOnResolve) {
- automap = addressmap_address_should_automap(socks->address, options);
- if (automap) {
+ /* Check the suffix... */
+ out->automap = addressmap_address_should_automap(socks->address, options);
+ if (out->automap) {
+ /* If we get here, then we should apply an automapping for this. */
const char *new_addr;
+ /* We return an IPv4 address by default, or an IPv6 address if we
+ * are allowed to do so. */
int addr_type = RESOLVED_TYPE_IPV4;
if (conn->socks_request->socks_version != 4) {
- if (!conn->ipv4_traffic_ok ||
- (conn->ipv6_traffic_ok && conn->prefer_ipv6_traffic) ||
- conn->prefer_ipv6_virtaddr)
+ if (!conn->entry_cfg.ipv4_traffic ||
+ (conn->entry_cfg.ipv6_traffic && conn->entry_cfg.prefer_ipv6) ||
+ conn->entry_cfg.prefer_ipv6_virtaddr)
addr_type = RESOLVED_TYPE_IPV6;
}
+ /* Okay, register the target address as automapped, and find the new
+ * address we're supposed to give as a resolve answer. (Return a cached
+ * value if we've looked up this address before.
+ */
new_addr = addressmap_register_virtual_address(
addr_type, tor_strdup(socks->address));
if (! new_addr) {
log_warn(LD_APP, "Unable to automap address %s",
escaped_safe_str(socks->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_INTERNAL);
- return -1;
+ out->end_reason = END_STREAM_REASON_INTERNAL;
+ out->should_close = 1;
+ return;
}
log_info(LD_APP, "Automapping %s to %s",
escaped_safe_str_client(socks->address),
@@ -974,28 +1016,35 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
}
+ /* Now handle reverse lookups, if they're in the cache. This doesn't
+ * happen too often, since client-side DNS caching is off by default. */
if (socks->command == SOCKS_COMMAND_RESOLVE_PTR) {
unsigned rewrite_flags = 0;
- if (conn->use_cached_ipv4_answers)
+ if (conn->entry_cfg.use_cached_ipv4_answers)
rewrite_flags |= AMR_FLAG_USE_IPV4_DNS;
- if (conn->use_cached_ipv6_answers)
+ if (conn->entry_cfg.use_cached_ipv6_answers)
rewrite_flags |= AMR_FLAG_USE_IPV6_DNS;
if (addressmap_rewrite_reverse(socks->address, sizeof(socks->address),
- rewrite_flags, &map_expires)) {
+ rewrite_flags, &out->map_expires)) {
char *result = tor_strdup(socks->address);
/* remember _what_ is supposed to have been resolved. */
tor_snprintf(socks->address, sizeof(socks->address), "REVERSE[%s]",
- orig_address);
+ out->orig_address);
connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_HOSTNAME,
strlen(result), (uint8_t*)result,
-1,
- map_expires);
- connection_mark_unattached_ap(conn,
- END_STREAM_REASON_DONE |
- END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
- return 0;
+ out->map_expires);
+ tor_free(result);
+ out->end_reason = END_STREAM_REASON_DONE |
+ END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED;
+ out->should_close = 1;
+ return;
}
+
+ /* Hang on, did we find an answer saying that this is a reverse lookup for
+ * an internal address? If so, we should reject it if we're condigured to
+ * do so. */
if (options->ClientDNSRejectInternalAddresses) {
/* Don't let people try to do a reverse lookup on 10.0.0.1. */
tor_addr_t addr;
@@ -1005,43 +1054,108 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
if (ok == 1 && tor_addr_is_internal(&addr, 0)) {
connection_ap_handshake_socks_resolved(conn, RESOLVED_TYPE_ERROR,
0, NULL, -1, TIME_MAX);
- connection_mark_unattached_ap(conn,
- END_STREAM_REASON_SOCKSPROTOCOL |
- END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED);
- return -1;
+ out->end_reason = END_STREAM_REASON_SOCKSPROTOCOL |
+ END_STREAM_REASON_FLAG_ALREADY_SOCKS_REPLIED;
+ out->should_close = 1;
+ return;
}
}
- } else if (!automap) {
- /* For address map controls, remap the address. */
- unsigned rewrite_flags = 0;
- if (conn->use_cached_ipv4_answers)
+ }
+
+ /* If we didn't automap it before, then this is still the address
+ * that came straight from the user, mapped according to any
+ * MapAddress/MAPADDRESS commands. Now other mappings, including
+ * previously registered Automap entries, TrackHostExits entries,
+ * and client-side DNS cache entries (not recommended).
+ */
+ if (socks->command != SOCKS_COMMAND_RESOLVE_PTR &&
+ !out->automap) {
+ unsigned rewrite_flags = AMR_FLAG_USE_AUTOMAP | AMR_FLAG_USE_TRACKEXIT;
+ addressmap_entry_source_t exit_source2;
+ if (conn->entry_cfg.use_cached_ipv4_answers)
rewrite_flags |= AMR_FLAG_USE_IPV4_DNS;
- if (conn->use_cached_ipv6_answers)
+ if (conn->entry_cfg.use_cached_ipv6_answers)
rewrite_flags |= AMR_FLAG_USE_IPV6_DNS;
if (addressmap_rewrite(socks->address, sizeof(socks->address),
- rewrite_flags, &map_expires, &exit_source)) {
+ rewrite_flags, &out->map_expires, &exit_source2)) {
control_event_stream_status(conn, STREAM_EVENT_REMAP,
REMAP_STREAM_SOURCE_CACHE);
}
+ if (out->exit_source == ADDRMAPSRC_NONE) {
+ /* If it wasn't a .exit before, maybe it turned into a .exit. Remember
+ * the original source of a .exit. */
+ out->exit_source = exit_source2;
+ }
}
- if (!automap && address_is_in_virtual_range(socks->address)) {
- /* This address was probably handed out by client_dns_get_unmapped_address,
- * but the mapping was discarded for some reason. We *don't* want to send
- * the address through Tor; that's likely to fail, and may leak
- * information.
+ /* Check to see whether we're about to use an address in the virtual
+ * range without actually having gotten it from an Automap. */
+ if (!out->automap && address_is_in_virtual_range(socks->address)) {
+ /* This address was probably handed out by
+ * client_dns_get_unmapped_address, but the mapping was discarded for some
+ * reason. Or the user typed in a virtual address range manually. We
+ * *don't* want to send the address through Tor; that's likely to fail,
+ * and may leak information.
*/
log_warn(LD_APP,"Missing mapping for virtual address '%s'. Refusing.",
safe_str_client(socks->address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_INTERNAL);
- return -1;
+ out->end_reason = END_STREAM_REASON_INTERNAL;
+ out->should_close = 1;
+ return;
+ }
+}
+
+/** Connection <b>conn</b> just finished its socks handshake, or the
+ * controller asked us to take care of it. If <b>circ</b> is defined,
+ * then that's where we'll want to attach it. Otherwise we have to
+ * figure it out ourselves.
+ *
+ * First, parse whether it's a .exit address, remap it, and so on. Then
+ * if it's for a general circuit, try to attach it to a circuit (or launch
+ * one as needed), else if it's for a rendezvous circuit, fetch a
+ * rendezvous descriptor first (or attach/launch a circuit if the
+ * rendezvous descriptor is already here and fresh enough).
+ *
+ * The stream will exit from the hop
+ * indicated by <b>cpath</b>, or from the last hop in circ's cpath if
+ * <b>cpath</b> is NULL.
+ */
+int
+connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
+ origin_circuit_t *circ,
+ crypt_path_t *cpath)
+{
+ socks_request_t *socks = conn->socks_request;
+ const or_options_t *options = get_options();
+ connection_t *base_conn = ENTRY_TO_CONN(conn);
+ time_t now = time(NULL);
+ rewrite_result_t rr;
+
+ memset(&rr, 0, sizeof(rr));
+ connection_ap_handshake_rewrite(conn,&rr);
+
+ if (rr.should_close) {
+ /* connection_ap_handshake_rewrite told us to close the connection,
+ * either because it sent back an answer, or because it sent back an
+ * error */
+ connection_mark_unattached_ap(conn, rr.end_reason);
+ if (END_STREAM_REASON_DONE == (rr.end_reason & END_STREAM_REASON_MASK))
+ return 0;
+ else
+ return -1;
}
+ const time_t map_expires = rr.map_expires;
+ const int automap = rr.automap;
+ const addressmap_entry_source_t exit_source = rr.exit_source;
+
/* Parse the address provided by SOCKS. Modify it in-place if it
* specifies a hidden-service (.onion) or particular exit node (.exit).
*/
- addresstype = parse_extended_hostname(socks->address);
+ const hostname_type_t addresstype = parse_extended_hostname(socks->address);
+ /* Now see whether the hostname is bogus. This could happen because of an
+ * onion hostname whose format we don't recognize. */
if (addresstype == BAD_HOSTNAME) {
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
@@ -1049,16 +1163,21 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ /* If this is a .exit hostname, strip off the .name.exit part, and
+ * see whether we're going to connect there, and otherwise handle it.
+ * (The ".exit" part got stripped off by "parse_extended_hostname").
+ *
+ * We'll set chosen_exit_name and/or close the connection as appropriate.
+ */
if (addresstype == EXIT_HOSTNAME) {
- /* foo.exit -- modify conn->chosen_exit_node to specify the exit
- * node, and conn->address to hold only the address portion. */
- char *s = strrchr(socks->address,'.');
-
- /* If StrictNodes is not set, then .exit overrides ExcludeNodes. */
+ /* If StrictNodes is not set, then .exit overrides ExcludeNodes but
+ * not ExcludeExitNodes. */
routerset_t *excludeset = options->StrictNodes ?
options->ExcludeExitNodesUnion_ : options->ExcludeExitNodes;
- const node_t *node;
+ const node_t *node = NULL;
+ /* If this .exit was added by an AUTOMAP, then it came straight from
+ * a user. Make sure that options->AllowDotExit permits that. */
if (exit_source == ADDRMAPSRC_AUTOMAP && !options->AllowDotExit) {
/* Whoops; this one is stale. It must have gotten added earlier,
* when AllowDotExit was on. */
@@ -1071,6 +1190,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ /* Double-check to make sure there are no .exits coming from
+ * impossible/weird sources. */
if (exit_source == ADDRMAPSRC_DNS ||
(exit_source == ADDRMAPSRC_NONE && !options->AllowDotExit)) {
/* It shouldn't be possible to get a .exit address from any of these
@@ -1085,9 +1206,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
tor_assert(!automap);
+ /* Now, find the character before the .(name) part. */
+ char *s = strrchr(socks->address,'.');
if (s) {
/* The address was of the form "(stuff).(name).exit */
if (s[1] != '\0') {
+ /* Looks like a real .exit one. */
conn->chosen_exit_name = tor_strdup(s+1);
node = node_get_by_nickname(conn->chosen_exit_name, 1);
@@ -1106,7 +1230,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
} else {
- /* It looks like they just asked for "foo.exit". */
+ /* It looks like they just asked for "foo.exit". That's a special
+ * form that means (foo's address).foo.exit. */
conn->chosen_exit_name = tor_strdup(socks->address);
node = node_get_by_nickname(conn->chosen_exit_name, 1);
@@ -1115,6 +1240,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
node_get_address_string(node, socks->address, sizeof(socks->address));
}
}
+
/* Now make sure that the chosen exit exists... */
if (!node) {
log_warn(LD_APP,
@@ -1136,8 +1262,12 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
implies no. */
}
+ /* Now, handle everything that isn't a .onion address. */
if (addresstype != ONION_HOSTNAME) {
- /* not a hidden-service request (i.e. normal or .exit) */
+ /* Not a hidden-service request. It's either a hostname or an IP,
+ * possibly with a .exit that we stripped off. */
+
+ /* Check for funny characters in the address. */
if (address_is_invalid_destination(socks->address, 1)) {
control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s",
escaped(socks->address));
@@ -1148,6 +1278,9 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+#ifdef ENABLE_TOR2WEB_MODE
+ /* If we're running in Tor2webMode, we don't allow anything BUT .onion
+ * addresses. */
if (options->Tor2webMode) {
log_warn(LD_APP, "Refusing to connect to non-hidden-service hostname %s "
"because tor2web mode is enabled.",
@@ -1155,13 +1288,17 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
}
+#endif
+ /* See if this is a hostname lookup that we can answer immediately.
+ * (For example, an attempt to look up the IP address for an IP address.)
+ */
if (socks->command == SOCKS_COMMAND_RESOLVE) {
tor_addr_t answer;
/* Reply to resolves immediately if we can. */
if (tor_addr_parse(&answer, socks->address) >= 0) {/* is it an IP? */
/* remember _what_ is supposed to have been resolved. */
- strlcpy(socks->address, orig_address, sizeof(socks->address));
+ strlcpy(socks->address, rr.orig_address, sizeof(socks->address));
connection_ap_handshake_socks_resolved_addr(conn, &answer, -1,
map_expires);
connection_mark_unattached_ap(conn,
@@ -1172,14 +1309,22 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
tor_assert(!automap);
rep_hist_note_used_resolve(now); /* help predict this next time */
} else if (socks->command == SOCKS_COMMAND_CONNECT) {
+ /* Special handling for attempts to connect */
tor_assert(!automap);
+ /* Don't allow connections to port 0. */
if (socks->port == 0) {
log_notice(LD_APP,"Application asked to connect to port 0. Refusing.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
return -1;
}
+ /* You can't make connections to internal addresses, by default.
+ * Exceptions are begindir requests (where the address is meaningless,
+ * or cases where you've hand-configured a particular exit, thereby
+ * making the local address meaningful. */
if (options->ClientRejectInternalAddresses &&
!conn->use_begindir && !conn->chosen_exit_name && !circ) {
+ /* If we reach this point then we don't want to allow internal
+ * addresses. Check if we got one. */
tor_addr_t addr;
if (tor_addr_hostname_is_local(socks->address) ||
(tor_addr_parse(&addr, socks->address) >= 0 &&
@@ -1214,39 +1359,58 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
connection_mark_unattached_ap(conn, END_STREAM_REASON_PRIVATE_ADDR);
return -1;
}
- }
+ } /* end "if we should check for internal addresses" */
+ /* Okay. We're still doing a CONNECT, and it wasn't a private
+ * address. Do special handling for literal IP addresses */
{
tor_addr_t addr;
/* XXX Duplicate call to tor_addr_parse. */
if (tor_addr_parse(&addr, socks->address) >= 0) {
+ /* If we reach this point, it's an IPv4 or an IPv6 address. */
sa_family_t family = tor_addr_family(&addr);
- if ((family == AF_INET && ! conn->ipv4_traffic_ok) ||
- (family == AF_INET6 && ! conn->ipv4_traffic_ok)) {
+
+ if ((family == AF_INET && ! conn->entry_cfg.ipv4_traffic) ||
+ (family == AF_INET6 && ! conn->entry_cfg.ipv6_traffic)) {
+ /* You can't do an IPv4 address on a v6-only socks listener,
+ * or vice versa. */
log_warn(LD_NET, "Rejecting SOCKS request for an IP address "
"family that this listener does not support.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
} else if (family == AF_INET6 && socks->socks_version == 4) {
+ /* You can't make a socks4 request to an IPv6 address. Socks4
+ * doesn't support that. */
log_warn(LD_NET, "Rejecting SOCKS4 request for an IPv6 address.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
- } else if (socks->socks_version == 4 && !conn->ipv4_traffic_ok) {
+ } else if (socks->socks_version == 4 &&
+ !conn->entry_cfg.ipv4_traffic) {
+ /* You can't do any kind of Socks4 request when IPv4 is forbidden.
+ *
+ * XXX raise this check outside the enclosing block? */
log_warn(LD_NET, "Rejecting SOCKS4 request on a listener with "
"no IPv4 traffic supported.");
connection_mark_unattached_ap(conn, END_STREAM_REASON_ENTRYPOLICY);
return -1;
} else if (family == AF_INET6) {
- conn->ipv4_traffic_ok = 0;
+ /* Tell the exit: we won't accept any ipv4 connection to an IPv6
+ * address. */
+ conn->entry_cfg.ipv4_traffic = 0;
} else if (family == AF_INET) {
- conn->ipv6_traffic_ok = 0;
+ /* Tell the exit: we won't accept any ipv6 connection to an IPv4
+ * address. */
+ conn->entry_cfg.ipv6_traffic = 0;
}
}
}
if (socks->socks_version == 4)
- conn->ipv6_traffic_ok = 0;
+ conn->entry_cfg.ipv6_traffic = 0;
+ /* Still handling CONNECT. Now, check for exit enclaves. (Which we
+ * don't do on BEGINDIR, or there is a chosen exit.)
+ */
if (!conn->use_begindir && !conn->chosen_exit_name && !circ) {
/* see if we can find a suitable enclave exit */
const node_t *r =
@@ -1263,11 +1427,13 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
}
- /* warn or reject if it's using a dangerous port */
+ /* Still handling CONNECT: warn or reject if it's using a dangerous
+ * port. */
if (!conn->use_begindir && !conn->chosen_exit_name && !circ)
if (consider_plaintext_ports(conn, socks->port) < 0)
return -1;
+ /* Remember the port so that we do predicted requests there. */
if (!conn->use_begindir) {
/* help predict this next time */
rep_hist_note_used_port(now, socks->port);
@@ -1276,25 +1442,41 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
rep_hist_note_used_resolve(now); /* help predict this next time */
/* no extra processing needed */
} else {
+ /* We should only be doing CONNECT or RESOLVE! */
tor_fragile_assert();
}
+
+ /* Okay. At this point we've set chosen_exit_name if needed, rewritten the
+ * address, and decided not to reject it for any number of reasons. Now
+ * mark the connection as waiting for a circuit, and try to attach it!
+ */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- if ((circ && connection_ap_handshake_attach_chosen_circuit(
- conn, circ, cpath) < 0) ||
- (!circ &&
- connection_ap_handshake_attach_circuit(conn) < 0)) {
+
+ /* If we were given a circuit to attach to, try to attach. Otherwise,
+ * try to find a good one and attach to that. */
+ int rv;
+ if (circ)
+ rv = connection_ap_handshake_attach_chosen_circuit(conn, circ, cpath);
+ else
+ rv = connection_ap_handshake_attach_circuit(conn);
+
+ /* If the above function returned 0 then we're waiting for a circuit.
+ * if it returned 1, we're attached. Both are okay. But if it returned
+ * -1, there was an error, so make sure the connection is marked, and
+ * return -1. */
+ if (rv < 0) {
if (!base_conn->marked_for_close)
connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
return -1;
}
+
return 0;
} else {
- /* it's a hidden-service request */
- rend_cache_entry_t *entry;
- int r;
- rend_service_authorization_t *client_auth;
- rend_data_t *rend_data;
+ /* If we get here, it's a request for a .onion address! */
tor_assert(!automap);
+
+ /* Check whether it's RESOLVE or RESOLVE_PTR. We don't handle those
+ * for hidden service addresses. */
if (SOCKS_COMMAND_IS_RESOLVE(socks->command)) {
/* if it's a resolve request, fail it right now, rather than
* building all the circuits and then realizing it won't work. */
@@ -1308,6 +1490,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ /* If we were passed a circuit, then we need to fail. .onion addresses
+ * only work when we launch our own circuits for now. */
if (circ) {
log_warn(LD_CONTROL, "Attachstream to a circuit is not "
"supported for .onion addresses currently. Failing.");
@@ -1315,15 +1499,22 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
- ENTRY_TO_EDGE_CONN(conn)->rend_data = rend_data =
+ /* Fill in the rend_data field so we can start doing a connection to
+ * a hidden service. */
+ rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
tor_malloc_zero(sizeof(rend_data_t));
strlcpy(rend_data->onion_address, socks->address,
sizeof(rend_data->onion_address));
log_info(LD_REND,"Got a hidden service request for ID '%s'",
safe_str_client(rend_data->onion_address));
- /* see if we already have it cached */
- r = rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
- if (r<0) {
+
+ /* see if we already have a hidden service descriptor cached for this
+ * address. */
+ rend_cache_entry_t *entry = NULL;
+ const int rend_cache_lookup_result =
+ rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
+ if (rend_cache_lookup_result < 0) {
+ /* We should already have rejected this address! */
log_warn(LD_BUG,"Invalid service name '%s'",
safe_str_client(rend_data->onion_address));
connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
@@ -1334,8 +1525,10 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
* a stable circuit yet, but we know we'll need *something*. */
rep_hist_note_used_internal(now, 0, 1);
- /* Look up if we have client authorization for it. */
- client_auth = rend_client_lookup_service_authorization(
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(
rend_data->onion_address);
if (client_auth) {
log_info(LD_REND, "Using previously configured client authorization "
@@ -1344,12 +1537,16 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
rend_data->auth_type = client_auth->auth_type;
}
- if (r==0) {
+
+ /* Now, we either launch an attempt to connect to the hidden service,
+ * or we launch an attempt to look up its descriptor, depending on
+ * whether we had the descriptor. */
+ if (rend_cache_lookup_result == 0) {
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
safe_str_client(rend_data->onion_address));
rend_client_refetch_v2_renddesc(rend_data);
- } else { /* r > 0 */
+ } else { /* rend_cache_lookup_result > 0 */
base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
log_info(LD_REND, "Descriptor is here. Great.");
if (connection_ap_handshake_attach_circuit(conn) < 0) {
@@ -1360,6 +1557,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
}
return 0;
}
+
return 0; /* unreached but keeps the compiler happy */
}
@@ -1391,7 +1589,7 @@ get_pf_socket(void)
}
#endif
-#if defined(TRANS_NETFILTER) || defined(TRANS_PF)
+#if defined(TRANS_NETFILTER) || defined(TRANS_PF) || defined(TRANS_TPROXY)
/** Try fill in the address of <b>req</b> from the socket configured
* with <b>conn</b>. */
static int
@@ -1401,13 +1599,45 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
socklen_t orig_dst_len = sizeof(orig_dst);
tor_addr_t addr;
+#ifdef TRANS_TRPOXY
+ if (options->TransProxyType_parsed == TPT_TPROXY) {
+ if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&orig_dst,
+ &orig_dst_len) < 0) {
+ int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
+ log_warn(LD_NET, "getsockname() failed: %s", tor_socket_strerror(e));
+ return -1;
+ }
+ goto done;
+ }
+#endif
+
#ifdef TRANS_NETFILTER
- if (getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
- (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) {
+ int rv = -1;
+ switch (ENTRY_TO_CONN(conn)->socket_family) {
+#ifdef TRANS_NETFILTER_IPV4
+ case AF_INET:
+ rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST,
+ (struct sockaddr*)&orig_dst, &orig_dst_len);
+ break;
+#endif
+#ifdef TRANS_NETFILTER_IPV6
+ case AF_INET6:
+ rv = getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IPV6, IP6T_SO_ORIGINAL_DST,
+ (struct sockaddr*)&orig_dst, &orig_dst_len);
+ break;
+#endif
+ default:
+ log_warn(LD_BUG,
+ "Received transparent data from an unsuported socket family %d",
+ ENTRY_TO_CONN(conn)->socket_family);
+ return -1;
+ }
+ if (rv < 0) {
int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s);
log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e));
return -1;
}
+ goto done;
#elif defined(TRANS_PF)
if (getsockname(ENTRY_TO_CONN(conn)->s, (struct sockaddr*)&orig_dst,
&orig_dst_len) < 0) {
@@ -1415,6 +1645,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
log_warn(LD_NET, "getsockname() failed: %s", tor_socket_strerror(e));
return -1;
}
+ goto done;
#else
(void)conn;
(void)req;
@@ -1422,6 +1653,7 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req)
return -1;
#endif
+ done:
tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port);
tor_addr_to_str(req->address, &addr, sizeof(req->address), 1);
@@ -1531,7 +1763,8 @@ connection_ap_get_original_destination(entry_connection_t *conn,
if (options->TransProxyType_parsed == TPT_PF_DIVERT)
return destination_from_socket(conn, req);
- if (options->TransProxyType_parsed == TPT_DEFAULT)
+ if (options->TransProxyType_parsed == TPT_DEFAULT ||
+ options->TransProxyType_parsed == TPT_IPFW)
return destination_from_pf(conn, req);
(void)conn;
@@ -1767,7 +2000,8 @@ connection_ap_supports_optimistic_data(const entry_connection_t *conn)
general circuit. */
if (edge_conn->on_circuit == NULL ||
edge_conn->on_circuit->state != CIRCUIT_STATE_OPEN ||
- edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL)
+ (edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_GENERAL &&
+ edge_conn->on_circuit->purpose != CIRCUIT_PURPOSE_C_REND_JOINED))
return 0;
return conn->may_use_optimistic_data;
@@ -1792,19 +2026,19 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
return 0;
/* If only IPv4 is supported, no flags */
- if (ap_conn->ipv4_traffic_ok && !ap_conn->ipv6_traffic_ok)
+ if (ap_conn->entry_cfg.ipv4_traffic && !ap_conn->entry_cfg.ipv6_traffic)
return 0;
if (! cpath_layer ||
! cpath_layer->extend_info)
return 0;
- if (!ap_conn->ipv4_traffic_ok)
+ if (!ap_conn->entry_cfg.ipv4_traffic)
flags |= BEGIN_FLAG_IPV4_NOT_OK;
exitnode = node_get_by_id(cpath_layer->extend_info->identity_digest);
- if (ap_conn->ipv6_traffic_ok && exitnode) {
+ if (ap_conn->entry_cfg.ipv6_traffic && exitnode) {
tor_addr_t a;
tor_addr_make_null(&a, AF_INET6);
if (compare_tor_addr_to_node_policy(&a, ap_conn->socks_request->port,
@@ -1819,7 +2053,7 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn)
if (flags == BEGIN_FLAG_IPV6_OK) {
/* When IPv4 and IPv6 are both allowed, consider whether to say we
* prefer IPv6. Otherwise there's no point in declaring a preference */
- if (ap_conn->prefer_ipv6_traffic)
+ if (ap_conn->entry_cfg.prefer_ipv6)
flags |= BEGIN_FLAG_IPV6_PREFERRED;
}
@@ -2056,8 +2290,8 @@ connection_ap_make_link(connection_t *partner,
/* Populate isolation fields. */
conn->socks_request->listener_type = CONN_TYPE_DIR_LISTENER;
conn->original_dest_address = tor_strdup(address);
- conn->session_group = session_group;
- conn->isolation_flags = isolation_flags;
+ conn->entry_cfg.session_group = session_group;
+ conn->entry_cfg.isolation_flags = isolation_flags;
base_conn->address = tor_strdup("(Tor_internal)");
tor_addr_make_unspec(&base_conn->addr);
@@ -2460,7 +2694,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
relay_header_unpack(&rh, cell->payload);
if (rh.length > RELAY_PAYLOAD_SIZE)
- return -1;
+ return -END_CIRC_REASON_TORPROTOCOL;
/* Note: we have to use relay_send_command_from_edge here, not
* connection_edge_end or connection_edge_send_command, since those require
@@ -2478,7 +2712,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
r = begin_cell_parse(cell, &bcell, &end_reason);
if (r < -1) {
- return -1;
+ return -END_CIRC_REASON_TORPROTOCOL;
} else if (r == -1) {
tor_free(bcell.address);
relay_send_end_cell_from_edge(rh.stream_id, circ, end_reason, NULL);
@@ -2576,15 +2810,31 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
n_stream->rend_data = rend_data_dup(origin_circ->rend_data);
tor_assert(connection_edge_is_rendezvous_stream(n_stream));
assert_circuit_ok(circ);
- if (rend_service_set_connection_addr_port(n_stream, origin_circ) < 0) {
+
+ const int r = rend_service_set_connection_addr_port(n_stream, origin_circ);
+ if (r < 0) {
log_info(LD_REND,"Didn't find rendezvous service (port %d)",
n_stream->base_.port);
+ /* Send back reason DONE because we want to make hidden service port
+ * scanning harder thus instead of returning that the exit policy
+ * didn't match, which makes it obvious that the port is closed,
+ * return DONE and kill the circuit. That way, a user (malicious or
+ * not) needs one circuit per bad port unless it matches the policy of
+ * the hidden service. */
relay_send_end_cell_from_edge(rh.stream_id, circ,
- END_STREAM_REASON_EXITPOLICY,
+ END_STREAM_REASON_DONE,
origin_circ->cpath->prev);
connection_free(TO_CONN(n_stream));
tor_free(address);
- return 0;
+
+ /* Drop the circuit here since it might be someone deliberately
+ * scanning the hidden service ports. Note that this mitigates port
+ * scanning by adding more work on the attacker side to successfully
+ * scan but does not fully solve it. */
+ if (r < -1)
+ return END_CIRC_AT_ORIGIN;
+ else
+ return 0;
}
assert_circuit_ok(circ);
log_debug(LD_REND,"Finished assigning addr/port");
@@ -2712,7 +2962,7 @@ connection_exit_connect(edge_connection_t *edge_conn)
const tor_addr_t *addr;
uint16_t port;
connection_t *conn = TO_CONN(edge_conn);
- int socket_error = 0;
+ int socket_error = 0, result;
if ( (!connection_edge_is_rendezvous_stream(edge_conn) &&
router_compare_to_my_exit_policy(&edge_conn->base_.addr,
@@ -2727,14 +2977,36 @@ connection_exit_connect(edge_connection_t *edge_conn)
return;
}
- addr = &conn->addr;
- port = conn->port;
+#ifdef HAVE_SYS_UN_H
+ if (conn->socket_family != AF_UNIX) {
+#else
+ {
+#endif /* defined(HAVE_SYS_UN_H) */
+ addr = &conn->addr;
+ port = conn->port;
+
+ if (tor_addr_family(addr) == AF_INET6)
+ conn->socket_family = AF_INET6;
+
+ log_debug(LD_EXIT, "about to try connecting");
+ result = connection_connect(conn, conn->address,
+ addr, port, &socket_error);
+#ifdef HAVE_SYS_UN_H
+ } else {
+ /*
+ * In the AF_UNIX case, we expect to have already had conn->port = 1,
+ * tor_addr_make_unspec(conn->addr) (cf. the way we mark in the incoming
+ * case in connection_handle_listener_read()), and conn->address should
+ * have the socket path to connect to.
+ */
+ tor_assert(conn->address && strlen(conn->address) > 0);
- if (tor_addr_family(addr) == AF_INET6)
- conn->socket_family = AF_INET6;
+ log_debug(LD_EXIT, "about to try connecting");
+ result = connection_connect_unix(conn, conn->address, &socket_error);
+#endif /* defined(HAVE_SYS_UN_H) */
+ }
- log_debug(LD_EXIT,"about to try connecting");
- switch (connection_connect(conn, conn->address, addr, port, &socket_error)) {
+ switch (result) {
case -1: {
int reason = errno_to_stream_end_reason(socket_error);
connection_edge_end(edge_conn, reason);
@@ -2764,7 +3036,6 @@ connection_exit_connect(edge_connection_t *edge_conn)
/* also, deliver a 'connected' cell back through the circuit. */
if (connection_edge_is_rendezvous_stream(edge_conn)) {
- /* rendezvous stream */
/* don't send an address back! */
connection_edge_send_command(edge_conn,
RELAY_COMMAND_CONNECTED,
@@ -2903,10 +3174,10 @@ connection_ap_can_use_exit(const entry_connection_t *conn, const node_t *exit)
addr_policy_result_t r;
if (0 == tor_addr_parse(&addr, conn->socks_request->address)) {
addrp = &addr;
- } else if (!conn->ipv4_traffic_ok && conn->ipv6_traffic_ok) {
+ } else if (!conn->entry_cfg.ipv4_traffic && conn->entry_cfg.ipv6_traffic) {
tor_addr_make_null(&addr, AF_INET6);
addrp = &addr;
- } else if (conn->ipv4_traffic_ok && !conn->ipv6_traffic_ok) {
+ } else if (conn->entry_cfg.ipv4_traffic && !conn->entry_cfg.ipv6_traffic) {
tor_addr_make_null(&addr, AF_INET);
addrp = &addr;
}
@@ -3012,7 +3283,7 @@ int
connection_edge_compatible_with_circuit(const entry_connection_t *conn,
const origin_circuit_t *circ)
{
- const uint8_t iso = conn->isolation_flags;
+ const uint8_t iso = conn->entry_cfg.isolation_flags;
const socks_request_t *sr = conn->socks_request;
/* If circ has never been used for an isolated connection, we can
@@ -3061,7 +3332,8 @@ connection_edge_compatible_with_circuit(const entry_connection_t *conn,
if ((iso & ISO_CLIENTADDR) &&
!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
return 0;
- if ((iso & ISO_SESSIONGRP) && conn->session_group != circ->session_group)
+ if ((iso & ISO_SESSIONGRP) &&
+ conn->entry_cfg.session_group != circ->session_group)
return 0;
if ((iso & ISO_NYM_EPOCH) && conn->nym_epoch != circ->nym_epoch)
return 0;
@@ -3100,7 +3372,7 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn,
circ->client_proto_type = conn->socks_request->listener_type;
circ->client_proto_socksver = conn->socks_request->socks_version;
tor_addr_copy(&circ->client_addr, &ENTRY_TO_CONN(conn)->addr);
- circ->session_group = conn->session_group;
+ circ->session_group = conn->entry_cfg.session_group;
circ->nym_epoch = conn->nym_epoch;
circ->socks_username = sr->username ?
tor_memdup(sr->username, sr->usernamelen) : NULL;
@@ -3127,7 +3399,7 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn,
mixed |= ISO_CLIENTPROTO;
if (!tor_addr_eq(&ENTRY_TO_CONN(conn)->addr, &circ->client_addr))
mixed |= ISO_CLIENTADDR;
- if (conn->session_group != circ->session_group)
+ if (conn->entry_cfg.session_group != circ->session_group)
mixed |= ISO_SESSIONGRP;
if (conn->nym_epoch != circ->nym_epoch)
mixed |= ISO_NYM_EPOCH;
@@ -3135,7 +3407,7 @@ connection_edge_update_circuit_isolation(const entry_connection_t *conn,
if (dry_run)
return mixed;
- if ((mixed & conn->isolation_flags) != 0) {
+ if ((mixed & conn->entry_cfg.isolation_flags) != 0) {
log_warn(LD_BUG, "Updating a circuit with seemingly incompatible "
"isolation flags.");
}