Commit f680d0fd authored by dana koch's avatar dana koch Committed by Nick Mathewson
Browse files

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.
parent 08ef8c09
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -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".)

+7 −1
Original line number Diff line number Diff line
@@ -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.");
+51 −13
Original line number Diff line number Diff line
@@ -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)
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;
+6 −1
Original line number Diff line number Diff line
@@ -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