From 3e4680f3126c2a06358535b08bf267dca1bb90a7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 3 Feb 2014 14:09:07 -0500 Subject: ipfw TransPort support on FreeBSD (10267) This isn't on by default; to get it, you need to set "TransProxyType ipfw". (The original patch had automatic detection for whether /dev/pf is present and openable, but that seems marginally fragile.) --- changes/10267_tproxy | 4 ++++ doc/tor.1.txt | 8 +++++--- src/or/config.c | 8 +++++++- src/or/connection_edge.c | 23 +++++++++++++++++++++++ src/or/or.h | 2 +- 5 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 changes/10267_tproxy diff --git a/changes/10267_tproxy b/changes/10267_tproxy new file mode 100644 index 0000000000..f65e4a2c5d --- /dev/null +++ b/changes/10267_tproxy @@ -0,0 +1,4 @@ + o Minor features: + - Support the ipfw firewall interface for transparent proxy support on + FreeBSD. To enable it, set "TransProxyType ipfw" in your torrc. + Resolves ticket 10267; patch from "yurivict". diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 1cc8f841ce..1a5ced8496 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1196,7 +1196,7 @@ The following options are useful only for clients (that is, if compatibility, TransListenAddress is only allowed when TransPort is just a port number.) -[[TransProxyType]] **TransProxyTYpe** **default**|**TPROXY**:: +[[TransProxyType]] **TransProxyTYpe** **default**|**TPROXY**|**ipfw**:: TransProxyType may only be enabled when there is transparent proxy listener enabled. + @@ -1208,8 +1208,10 @@ The following options are useful only for clients (that is, if feature can be found in the Linux kernel source tree in the file Documentation/networking/tproxy.txt. + - Set this to "default", or leave it unconfigured, to use regular IPTables - on Linux, or to use pf on the *BSD operating systems. + Set this option to "ipfw" to use the FreeBSD ipfw interface. + + + Set this option to "default", or leave it unconfigured, to use regular + IPTables on Linux, or to use pf on the *BSD operating systems. + (Default: "default".) diff --git a/src/or/config.c b/src/or/config.c index 4a6b30172c..55a23b1ce3 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) { + if (options->TransPort_set && options->TransProxyType_parsed != TPT_IPFW) { if (get_pf_socket() < 0) { *msg = tor_strdup("Unable to open /dev/pf for transparent proxy."); goto rollback; @@ -2558,6 +2558,12 @@ options_validate(or_options_t *old_options, or_options_t *options, REJECT("TPROXY is a Linux-specific feature."); #else options->TransProxyType_parsed = TPT_TPROXY; +#endif + } else if (!strcasecmp(options->TransProxyType, "ipfw")) { +#ifndef __FreeBSD__ + REJECT("ipfw is a FreeBSD-specific feature."); +#else + options->TransProxyType_parsed = TPT_IPFW; #endif } else { REJECT("Unrecognized value for TransProxyType"); diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 41ca6119b0..ddeac10381 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1435,6 +1435,29 @@ connection_ap_get_original_destination(entry_connection_t *conn, return -1; } +#ifdef __FreeBSD__ + if (get_options()->TransProxyType_parsed == TPT_IPFW) { + /* ipfw(8) is used and in this case getsockname returned the original + destination */ + if (proxy_sa->sa_family == AF_INET) { + struct sockaddr_in *dest_addr4 = (struct sockaddr_in *)proxy_sa; + tor_addr_from_ipv4n(&addr, dest_addr4->sin_addr.s_addr); + req->port = ntohs(dest_addr4->sin_port); + } else if (proxy_sa->sa_family == AF_INET6) { + struct sockaddr_in6 *dest_addr6 = (struct sockaddr_in6 *)proxy_sa; + tor_addr_from_in6(&addr, &dest_addr6->sin6_addr); + req->port = ntohs(dest_addr6->sin6_port); + } else { + tor_fragile_assert(); + return -1; + } + + tor_addr_to_str(req->address, &addr, sizeof(req->address), 0); + + return 0; + } +#endif + memset(&pnl, 0, sizeof(pnl)); pnl.proto = IPPROTO_TCP; pnl.direction = PF_OUT; diff --git a/src/or/or.h b/src/or/or.h index 38ab1767e0..a5e2e7069d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3461,7 +3461,7 @@ typedef struct { const char *TransProxyType; /**< What kind of transparent proxy * implementation are we using? */ /** Parsed value of TransProxyType. */ - enum { TPT_DEFAULT, TPT_TPROXY } TransProxyType_parsed; + enum { TPT_DEFAULT, TPT_TPROXY, TPT_IPFW } 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-54-g00ecf From 08ef8c0958ebeb134e4f29d1738c85c0ac81e71d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Mon, 3 Feb 2014 14:13:08 -0500 Subject: tor_addr_from_sockaddr() is applicable in ipfw code, so use it. --- src/or/connection_edge.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index ddeac10381..2ece2e44e3 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1439,15 +1439,7 @@ connection_ap_get_original_destination(entry_connection_t *conn, if (get_options()->TransProxyType_parsed == TPT_IPFW) { /* ipfw(8) is used and in this case getsockname returned the original destination */ - if (proxy_sa->sa_family == AF_INET) { - struct sockaddr_in *dest_addr4 = (struct sockaddr_in *)proxy_sa; - tor_addr_from_ipv4n(&addr, dest_addr4->sin_addr.s_addr); - req->port = ntohs(dest_addr4->sin_port); - } else if (proxy_sa->sa_family == AF_INET6) { - struct sockaddr_in6 *dest_addr6 = (struct sockaddr_in6 *)proxy_sa; - tor_addr_from_in6(&addr, &dest_addr6->sin6_addr); - req->port = ntohs(dest_addr6->sin6_port); - } else { + if (tor_addr_from_sockaddr(&addr, proxy_sa, &req->port) < 0) { tor_fragile_assert(); return -1; } -- cgit v1.2.3-54-g00ecf 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. --- doc/tor.1.txt | 17 +++++++++---- src/or/config.c | 8 +++++- src/or/connection_edge.c | 64 ++++++++++++++++++++++++++++++++++++++---------- src/or/or.h | 7 +++++- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 1a5ced8496..d1556a5541 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1196,12 +1196,12 @@ The following options are useful only for clients (that is, if compatibility, TransListenAddress is only allowed when TransPort is just a port number.) -[[TransProxyType]] **TransProxyTYpe** **default**|**TPROXY**|**ipfw**:: +[[TransProxyType]] **TransProxyTYpe** **default**|**TPROXY**|**ipfw**|**pf-divert**:: TransProxyType may only be enabled when there is transparent proxy listener enabled. + - Set this to TPROXY if you wish to be able to use the TPROXY Linux module to - transparently proxy connections that are configured using the TransPort + Set this to "TPROXY" if you wish to be able to use the TPROXY Linux module + to transparently proxy connections that are configured using the TransPort option. This setting lets the listener on the TransPort accept connections for all addresses, even when the TransListenAddress is configured for an internal address. Detailed information on how to configure the TPROXY @@ -1210,8 +1210,15 @@ The following options are useful only for clients (that is, if + Set this option to "ipfw" to use the FreeBSD ipfw interface. + - Set this option to "default", or leave it unconfigured, to use regular - IPTables on Linux, or to use pf on the *BSD operating systems. + On *BSD operating systems when using pf, set this to "pf-divert" to take + advantage of +divert-to+ rules, which do not modify the packets like + +rdr-to+ rules do. Detailed information on how to configure pf to use + +divert-to+ rules can be found in the pf.conf(5) manual page. On OpenBSD, + +divert-to+ is available to use on versions greater than or equal to + OpenBSD 4.4. + + + Set this to "default", or leave it unconfigured, to use regular IPTables + on Linux, or to use pf +rdr-to+ rules on *BSD systems. + (Default: "default".) diff --git a/src/or/config.c b/src/or/config.c index 55a23b1ce3..e22d3b86e0 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 2ece2e44e3..04e60b9139 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 a5e2e7069d..5510af723e 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-54-g00ecf From db8259c23035a802610ece3dd437cd3ccc32ac9d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 3 Apr 2014 16:41:54 -0400 Subject: Whitespace, doc fixes --- src/or/config.c | 3 ++- src/or/connection_edge.c | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index e22d3b86e0..f0ee322987 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1089,7 +1089,8 @@ 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_DEFAULT) { + 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; diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 04e60b9139..f294842177 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1391,7 +1391,9 @@ get_pf_socket(void) } #endif -static int +/** Try fill in the address of req from the socket configured + * with conn. */ +static int destination_from_socket(entry_connection_t *conn, socks_request_t *req) { struct sockaddr_storage orig_dst; @@ -1506,7 +1508,6 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req) 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. -- cgit v1.2.3-54-g00ecf From c00c45fee133cbb21f69321fa7715a9f7d91164d Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 3 Apr 2014 16:54:31 -0400 Subject: Fix OSX compilation. --- src/or/connection_edge.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index f294842177..a8ad9ec2e2 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1391,6 +1391,7 @@ get_pf_socket(void) } #endif +#if defined(TRANS_NETFILTER) || defined(TRANS_PF) /** Try fill in the address of req from the socket configured * with conn. */ static int @@ -1426,7 +1427,9 @@ destination_from_socket(entry_connection_t *conn, socks_request_t *req) return 0; } +#endif +#ifdef TRANS_PF static int destination_from_pf(entry_connection_t *conn, socks_request_t *req) { @@ -1507,6 +1510,7 @@ destination_from_pf(entry_connection_t *conn, socks_request_t *req) return 0; } +#endif /** Fetch the original destination address and port from a * system-specific interface and put them into a -- cgit v1.2.3-54-g00ecf From 89e520e2a710267c32b5afa03d32924646f78b28 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 3 Apr 2014 17:00:22 -0400 Subject: Call pf-divert openbsd-specific, not no-linux --- src/or/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index f0ee322987..3179018fb6 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -2555,8 +2555,8 @@ options_validate(or_options_t *old_options, or_options_t *options, 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."); +#ifndef __OpenBSD__ + REJECT("pf-divert is a OpenBSD-specific feature."); #else options->TransProxyType_parsed = TPT_PF_DIVERT; #endif -- cgit v1.2.3-54-g00ecf From f9719b078141fb1a4a0eee281031c73cffde7c30 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 3 Apr 2014 17:05:31 -0400 Subject: Changes file for 10896 --- changes/10896 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 changes/10896 diff --git a/changes/10896 b/changes/10896 new file mode 100644 index 0000000000..278815e766 --- /dev/null +++ b/changes/10896 @@ -0,0 +1,8 @@ + o Minor features: + - Support OpenBSD's divert-to rules with the pf firewall, when + "TransProxyType pf-divert" is specified. This allows Tor to run a + TransPort transparent proxy port on OpenBSD 4.4 or later without + root privileges. See the pf.conf(5) manual page for information on + configuring pf to use divert-to rules. Closes ticket 10896; patch + from Dana Koch. + -- cgit v1.2.3-54-g00ecf