From f680d0fdd2e42bce109219ed78d0527d16995415 Mon Sep 17 00:00:00 2001 From: dana koch Date: Mon, 10 Feb 2014 21:23:51 +1100 Subject: Educate tor on OpenBSD's use of divert-to rules with the pf firewall. This means that tor can run without needing to communicate with ioctls to the firewall, and therefore doesn't need to run with privileges to open the /dev/pf device node. A new TransProxyType is added for this purpose, "pf-divert"; if the user specifies this TransProxyType in their torrc, then the pf device node is never opened and the connection destination is determined with getsockname (as per pf(4)). The default behaviour (ie., when TransProxyType is "default" when using the pf firewall) is still to assume that pf is configured with rdr-to rules. --- src/or/config.c | 8 +++++- src/or/connection_edge.c | 64 ++++++++++++++++++++++++++++++++++++++---------- src/or/or.h | 7 +++++- 3 files changed, 64 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/or/config.c b/src/or/config.c index 55a23b1ce..e22d3b86e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1089,7 +1089,7 @@ options_act_reversible(const or_options_t *old_options, char **msg) #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) /* Open /dev/pf before dropping privileges. */ - if (options->TransPort_set && options->TransProxyType_parsed != TPT_IPFW) { + if (options->TransPort_set && options->TransProxyType_parsed == TPT_DEFAULT) { if (get_pf_socket() < 0) { *msg = tor_strdup("Unable to open /dev/pf for transparent proxy."); goto rollback; @@ -2553,6 +2553,12 @@ options_validate(or_options_t *old_options, or_options_t *options, if (options->TransProxyType) { if (!strcasecmp(options->TransProxyType, "default")) { options->TransProxyType_parsed = TPT_DEFAULT; + } else if (!strcasecmp(options->TransProxyType, "pf-divert")) { +#ifdef __linux__ + REJECT("pf is a BSD-specific feature."); +#else + options->TransProxyType_parsed = TPT_PF_DIVERT; +#endif } else if (!strcasecmp(options->TransProxyType, "tproxy")) { #ifndef __linux__ REJECT("TPROXY is a Linux-specific feature."); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 2ece2e44e..04e60b913 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1391,35 +1391,43 @@ get_pf_socket(void) } #endif -/** Fetch the original destination address and port from a - * system-specific interface and put them into a - * socks_request_t as if they came from a socks request. - * - * Return -1 if an error prevents fetching the destination, - * else return 0. - */ -static int -connection_ap_get_original_destination(entry_connection_t *conn, - socks_request_t *req) +static int +destination_from_socket(entry_connection_t *conn, socks_request_t *req) { -#ifdef TRANS_NETFILTER - /* Linux 2.4+ */ struct sockaddr_storage orig_dst; socklen_t orig_dst_len = sizeof(orig_dst); tor_addr_t addr; +#ifdef TRANS_NETFILTER if (getsockopt(ENTRY_TO_CONN(conn)->s, SOL_IP, SO_ORIGINAL_DST, (struct sockaddr*)&orig_dst, &orig_dst_len) < 0) { int e = tor_socket_errno(ENTRY_TO_CONN(conn)->s); log_warn(LD_NET, "getsockopt() failed: %s", tor_socket_strerror(e)); return -1; } +#elif defined(TRANS_PF) + 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; + } +#else + (void)conn; + (void)req; + log_warn(LD_BUG, "Unable to determine destination from socket."); + return -1; +#endif tor_addr_from_sockaddr(&addr, (struct sockaddr*)&orig_dst, &req->port); tor_addr_to_str(req->address, &addr, sizeof(req->address), 1); return 0; -#elif defined(TRANS_PF) +} + +static int +destination_from_pf(entry_connection_t *conn, socks_request_t *req) +{ struct sockaddr_storage proxy_addr; socklen_t proxy_addr_len = sizeof(proxy_addr); struct sockaddr *proxy_sa = (struct sockaddr*) &proxy_addr; @@ -1496,6 +1504,36 @@ connection_ap_get_original_destination(entry_connection_t *conn, req->port = ntohs(pnl.rdport); return 0; +} + + +/** Fetch the original destination address and port from a + * system-specific interface and put them into a + * socks_request_t as if they came from a socks request. + * + * Return -1 if an error prevents fetching the destination, + * else return 0. + */ +static int +connection_ap_get_original_destination(entry_connection_t *conn, + socks_request_t *req) +{ +#ifdef TRANS_NETFILTER + return destination_from_socket(conn, req); +#elif defined(TRANS_PF) + const or_options_t *options = get_options(); + + if (options->TransProxyType_parsed == TPT_PF_DIVERT) + return destination_from_socket(conn, req); + + if (options->TransProxyType_parsed == TPT_DEFAULT) + return destination_from_pf(conn, req); + + (void)conn; + (void)req; + log_warn(LD_BUG, "Proxy destination determination mechanism %s unknown.", + options->TransProxyType); + return -1; #else (void)conn; (void)req; diff --git a/src/or/or.h b/src/or/or.h index a5e2e7069..5510af723 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3461,7 +3461,12 @@ typedef struct { const char *TransProxyType; /**< What kind of transparent proxy * implementation are we using? */ /** Parsed value of TransProxyType. */ - enum { TPT_DEFAULT, TPT_TPROXY, TPT_IPFW } TransProxyType_parsed; + enum { + TPT_DEFAULT, + TPT_PF_DIVERT, + TPT_IPFW, + TPT_TPROXY, + } TransProxyType_parsed; config_line_t *NATDPort_lines; /**< Ports to listen on for transparent natd * connections. */ config_line_t *ControlPort_lines; /**< Ports to listen on for control -- cgit v1.2.3