From 8d59ddf3cba541c6578dff121e8f0623a7606bab Mon Sep 17 00:00:00 2001 From: Jacob Appelbaum Date: Mon, 11 Aug 2014 12:27:04 -0700 Subject: [PATCH 01/12] Commit second draft of Jake's SOCKS5-over-AF_UNIX patch. See ticket #12585. Signed-off-by: Andrea Shepard --- changes/bug12585 | 9 ++ doc/tor.1.txt | 9 ++ src/common/address.c | 18 +++ src/or/config.c | 24 ++++ src/or/connection.c | 244 +++++++++++++++++++++++++++++++-------- src/or/connection_edge.c | 6 +- src/or/main.c | 4 + src/or/or.h | 8 ++ src/or/relay.c | 5 +- 9 files changed, 274 insertions(+), 53 deletions(-) create mode 100644 changes/bug12585 diff --git a/changes/bug12585 b/changes/bug12585 new file mode 100644 index 0000000000..ccdcd17e6c --- /dev/null +++ b/changes/bug12585 @@ -0,0 +1,9 @@ + o Major features (security) + - Implementation of SocksSocket option - SocksSocket implements a SOCKS + proxy reachable by Unix Domain Socket. This allows client applications to + communicate with Tor without having the ability to create AF_INET or + AF_INET6 family sockets. If an application has permission to create a socket + with AF_UNIX, it may directly communicate with Tor as if it were an other + SOCKS proxy. This should allow high risk applications to be entirely prevented + from connecting directly with TCP/IP, they will be able to only connect to the + internet through AF_UNIX and only through Tor. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 9e86a67359..43aade9d9e 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -483,6 +483,15 @@ GENERAL OPTIONS in accordance to RFC 1929. Both username and password must be between 1 and 255 characters. +[[SocksSocket]] **SocksSocket** __Path__:: + Like SocksPort, but listens on a Unix domain socket, rather than a TCP + socket. (Unix and Unix-like systems only.) + +[[SocksSocketsGroupWritable]] **SocksSocketsGroupWritable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read and + write unix sockets (e.g. SocksSocket). If the option is set to 1, make + the SocksSocket socket readable and writable by the default GID. (Default: 0) + [[KeepalivePeriod]] **KeepalivePeriod** __NUM__:: To keep firewalls from expiring connections, send a padding keepalive cell every NUM seconds on open connections that are in use. If the connection diff --git a/src/common/address.c b/src/common/address.c index a80926049a..1c3777fa82 100644 --- a/src/common/address.c +++ b/src/common/address.c @@ -121,6 +121,15 @@ tor_addr_to_sockaddr(const tor_addr_t *a, } } +/** Set address a to zero. This address belongs to + * the AF_UNIX family. */ +static void +tor_addr_make_af_unix(tor_addr_t *a) +{ + memset(a, 0, sizeof(*a)); + a->family = AF_UNIX; +} + /** Set the tor_addr_t in a to contain the socket address contained in * sa. */ int @@ -142,6 +151,9 @@ tor_addr_from_sockaddr(tor_addr_t *a, const struct sockaddr *sa, tor_addr_from_in6(a, &sin6->sin6_addr); if (port_out) *port_out = ntohs(sin6->sin6_port); + } else if (sa->sa_family == AF_UNIX) { + tor_addr_make_af_unix(a); + return 0; } else { tor_addr_make_unspec(a); return -1; @@ -421,6 +433,10 @@ tor_addr_to_str(char *dest, const tor_addr_t *addr, size_t len, int decorate) ptr = dest; } break; + case AF_UNIX: + tor_snprintf(dest, len, "AF_UNIX"); + ptr = dest; + break; default: return NULL; } @@ -816,6 +832,8 @@ tor_addr_is_null(const tor_addr_t *addr) } case AF_INET: return (tor_addr_to_ipv4n(addr) == 0); + case AF_UNIX: + return 1; case AF_UNSPEC: return 1; default: diff --git a/src/or/config.c b/src/or/config.c index 2fa077e146..e080e7749b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -190,6 +190,8 @@ static config_var_t option_vars_[] = { V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), V(ControlSocketsGroupWritable, BOOL, "0"), + V(SocksSocket, LINELIST, NULL), + V(SocksSocketsGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), @@ -1030,6 +1032,20 @@ options_act_reversible(const or_options_t *old_options, char **msg) } #endif +#ifndef HAVE_SYS_UN_H + if (options->SocksSocket || options->SocksSocketsGroupWritable) { + *msg = tor_strdup("Unix domain sockets (SocksSocket) not supported " + "on this OS/with this build."); + goto rollback; + } +#else + if (options->SocksSocketsGroupWritable && !options->SocksSocket) { + *msg = tor_strdup("Setting SocksSocketGroupWritable without setting" + "a SocksSocket makes no sense."); + goto rollback; + } +#endif + if (running_tor) { int n_ports=0; /* We need to set the connection limit before we can open the listeners. */ @@ -6120,6 +6136,12 @@ parse_ports(or_options_t *options, int validate_only, *msg = tor_strdup("Invalid ControlSocket configuration"); goto err; } + if (parse_unix_socket_config(ports, + options->SocksSocket, + CONN_TYPE_AP_LISTENER) < 0) { + *msg = tor_strdup("Invalid SocksSocket configuration"); + goto err; + } } if (! options->ClientOnly) { if (parse_port_config(ports, @@ -6163,6 +6185,8 @@ parse_ports(or_options_t *options, int validate_only, !! count_real_listeners(ports, CONN_TYPE_OR_LISTENER); options->SocksPort_set = !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); + options->SocksSocket_set = + !! count_real_listeners(ports, CONN_TYPE_AP_LISTENER); options->TransPort_set = !! count_real_listeners(ports, CONN_TYPE_AP_TRANS_LISTENER); options->NATDPort_set = diff --git a/src/or/connection.c b/src/or/connection.c index c67cc3c111..ad7e6de9b6 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -308,6 +308,8 @@ entry_connection_new(int type, int socket_family) entry_conn->ipv4_traffic_ok = 1; else if (socket_family == AF_INET6) entry_conn->ipv6_traffic_ok = 1; + else if (socket_family == AF_UNIX) + entry_conn->is_socks_socket = 1; return entry_conn; } @@ -516,9 +518,10 @@ connection_free_(connection_t *conn) buf_free(conn->outbuf); } else { if (conn->socket_family == AF_UNIX) { - /* For now only control ports can be Unix domain sockets + /* For now only control and SOCKS ports can be Unix domain sockets * and listeners at the same time */ - tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); + tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER || + conn->type == CONN_TYPE_AP_LISTENER); if (unlink(conn->address) < 0 && errno != ENOENT) { log_warn(LD_NET, "Could not unlink %s: %s", conn->address, @@ -954,6 +957,47 @@ check_location_for_unix_socket(const or_options_t *options, const char *path) } #endif +#ifdef HAVE_SYS_UN_H +/** Check whether we should be willing to open an AF_UNIX socket in + * path. Return 0 if we should go ahead and -1 if we shouldn't. */ +static int +check_location_for_socks_unix_socket(const or_options_t *options, + const char *path) +{ + int r = -1; + char *p = tor_strdup(path); + cpd_check_t flags = CPD_CHECK_MODE_ONLY; + if (get_parent_directory(p)<0 || p[0] != '/') { + log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " + "relative paths for unix sockets.", path); + goto done; + } + + if (options->SocksSocketsGroupWritable) + flags |= CPD_GROUP_OK; + + if (check_private_dir(p, flags, options->User) < 0) { + char *escpath, *escdir; + escpath = esc_for_log(path); + escdir = esc_for_log(p); + log_warn(LD_GENERAL, "Before Tor can create a SocksSocket in %s, the " + "directory %s needs to exist, and to be accessible only by the " + "user%s account that is running Tor. (On some Unix systems, " + "anybody who can list a socket can connect to it, so Tor is " + "being careful.)", escpath, escdir, + options->SocksSocketsGroupWritable ? " and group" : ""); + tor_free(escpath); + tor_free(escdir); + goto done; + } + + r = 0; + done: + tor_free(p); + return r; +} +#endif + /** Tell the TCP stack that it shouldn't wait for a long time after * sock has closed before reusing its port. Return 0 on success, * -1 on failure. */ @@ -1029,30 +1073,103 @@ connection_listener_new(const struct sockaddr *listensockaddr, } if (listensockaddr->sa_family == AF_INET || - listensockaddr->sa_family == AF_INET6) { + listensockaddr->sa_family == AF_INET6 || + (listensockaddr->sa_family == AF_UNIX && + type != CONN_TYPE_CONTROL_LISTENER)) { int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); if (is_tcp) start_reading = 1; - tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); + if ( listensockaddr->sa_family == AF_INET || + listensockaddr->sa_family == AF_INET6) { - log_notice(LD_NET, "Opening %s on %s", - conn_type_to_string(type), fmt_addrport(&addr, usePort)); + tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); - s = tor_open_socket_nonblocking(tor_addr_family(&addr), - is_tcp ? SOCK_STREAM : SOCK_DGRAM, - is_tcp ? IPPROTO_TCP: IPPROTO_UDP); - if (!SOCKET_OK(s)) { - log_warn(LD_NET,"Socket creation failed: %s", - tor_socket_strerror(tor_socket_errno(-1))); - goto err; + log_notice(LD_NET, "Opening %s on %s", + conn_type_to_string(type), fmt_addrport(&addr, usePort)); + + s = tor_open_socket_nonblocking(tor_addr_family(&addr), + is_tcp ? SOCK_STREAM : SOCK_DGRAM, + is_tcp ? IPPROTO_TCP: IPPROTO_UDP); + if (!SOCKET_OK(s)) { + log_warn(LD_NET, "Socket creation failed: %s", + tor_socket_strerror(tor_socket_errno(-1))); + goto err; + } + + if (make_socket_reuseable(s) < 0) { + log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", + conn_type_to_string(type), + tor_socket_strerror(errno)); + } } - if (make_socket_reuseable(s) < 0) { - log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", - conn_type_to_string(type), - tor_socket_strerror(errno)); +#ifdef HAVE_SYS_UN_H + if (listensockaddr->sa_family == AF_UNIX && + type != CONN_TYPE_CONTROL_LISTENER) { + tor_assert(listensockaddr->sa_family == AF_UNIX); + + if (check_location_for_socks_unix_socket(options, address) < 0) + goto err; + + log_notice(LD_NET, "Opening SocksSocket %s on %s", + conn_type_to_string(type), address); + + tor_addr_make_unspec(&addr); + + if (unlink(address) < 0 && errno != ENOENT) { + log_warn(LD_NET, "Could not unlink %s: %s", address, + strerror(errno)); + goto err; + } + + s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); + if (! SOCKET_OK(s)) { + log_warn(LD_NET, "SocksSocket socket creation failed: %s.", + strerror(errno)); + goto err; + } + + if (bind(s, listensockaddr, + (socklen_t)sizeof(struct sockaddr_un)) == -1) { + log_warn(LD_NET, "Bind to %s failed: %s.", address, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } +#ifdef HAVE_PWD_H + if (options->User) { + pw = getpwnam(options->User); + if (pw == NULL) { + log_warn(LD_NET, + "Unable to chown() %s socket: user %s not found.", + address, options->User); + goto err; + } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) { + log_warn(LD_NET, "Unable to chown() %s socket: %s.", + address, strerror(errno)); + goto err; + } + } +#endif + + if (options->SocksSocketsGroupWritable) { + /* We need to use chmod; fchmod doesn't work on sockets on all + * platforms. */ + if (chmod(address, 0660) < 0) { + log_warn(LD_FS, "Unable to make %s group-writable.", address); + goto err; + } + } + + if (listen(s, SOMAXCONN) < 0) { + log_warn(LD_NET, "Could not listen on %s: %s", address, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } } +#else + (void)options; +#endif /* HAVE_SYS_UN_H */ #if defined USE_TRANSPARENT && defined(IP_TRANSPARENT) if (options->TransProxyType_parsed == TPT_TPROXY && @@ -1090,48 +1207,53 @@ connection_listener_new(const struct sockaddr *listensockaddr, } #endif - if (bind(s,listensockaddr,socklen) < 0) { - const char *helpfulhint = ""; - int e = tor_socket_errno(s); - if (ERRNO_IS_EADDRINUSE(e)) - helpfulhint = ". Is Tor already running?"; - log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, - tor_socket_strerror(e), helpfulhint); - goto err; - } - - if (is_tcp) { - if (tor_listen(s) < 0) { - log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, - tor_socket_strerror(tor_socket_errno(s))); + if (listensockaddr->sa_family != AF_UNIX) { + if (bind(s,listensockaddr,socklen) < 0) { + const char *helpfulhint = ""; + int e = tor_socket_errno(s); + if (ERRNO_IS_EADDRINUSE(e)) + helpfulhint = ". Is Tor already running?"; + log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, + tor_socket_strerror(e), helpfulhint); goto err; } - } - if (usePort != 0) { - gotPort = usePort; - } else { - tor_addr_t addr2; - struct sockaddr_storage ss; - socklen_t ss_len=sizeof(ss); - if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { - log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", - conn_type_to_string(type), - tor_socket_strerror(tor_socket_errno(s))); - gotPort = 0; + if (is_tcp) { + if (tor_listen(s) < 0) { + log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, + tor_socket_strerror(tor_socket_errno(s))); + goto err; + } + } + + if (usePort != 0) { + gotPort = usePort; + } else { + tor_addr_t addr2; + struct sockaddr_storage ss; + socklen_t ss_len=sizeof(ss); + if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { + log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", + conn_type_to_string(type), + tor_socket_strerror(tor_socket_errno(s))); + gotPort = 0; + } + tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); } - tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); } #ifdef HAVE_SYS_UN_H - } else if (listensockaddr->sa_family == AF_UNIX) { + } else if (listensockaddr->sa_family == AF_UNIX && + type != CONN_TYPE_AP_LISTENER) { start_reading = 1; /* For now only control ports can be Unix domain sockets * and listeners at the same time */ tor_assert(type == CONN_TYPE_CONTROL_LISTENER); - if (check_location_for_unix_socket(options, address) < 0) - goto err; + if ( type == CONN_TYPE_CONTROL_LISTENER ) { + if (check_location_for_unix_socket(options, address) < 0) + goto err; + } log_notice(LD_NET, "Opening %s on %s", conn_type_to_string(type), address); @@ -1177,6 +1299,15 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } + if (options->SocksSocketsGroupWritable) { + /* We need to use chmod; fchmod doesn't work on sockets on all + * platforms. */ + if (chmod(address, 0660) < 0) { + log_warn(LD_FS,"Unable to make %s group-writable.", address); + goto err; + } + } + if (listen(s, SOMAXCONN) < 0) { log_warn(LD_NET, "Could not listen on %s: %s", address, tor_socket_strerror(tor_socket_errno(s))); @@ -1294,6 +1425,8 @@ check_sockaddr(const struct sockaddr *sa, int len, int level) "Address for new connection has address/port equal to zero."); ok = 0; } + } else if (sa->sa_family == AF_UNIX) { + ok = 1; } else { ok = 0; } @@ -1378,7 +1511,8 @@ connection_handle_listener_read(connection_t *conn, int new_type) return 0; } - if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6) { + if (conn->socket_family == AF_INET || conn->socket_family == AF_INET6 || + (conn->socket_family == AF_UNIX && new_type == CONN_TYPE_AP)) { tor_addr_t addr; uint16_t port; if (check_sockaddr(remote, remotelen, LOG_INFO)<0) { @@ -1419,7 +1553,16 @@ connection_handle_listener_read(connection_t *conn, int new_type) newconn->port = port; newconn->address = tor_dup_addr(&addr); - if (new_type == CONN_TYPE_AP) { + if (new_type == CONN_TYPE_AP && conn->socket_family != AF_UNIX) { + log_notice(LD_NET, "New SOCKS connection opened from %s.", + fmt_and_decorate_addr(&addr)); + TO_ENTRY_CONN(newconn)->socks_request->socks_prefer_no_auth = + TO_LISTENER_CONN(conn)->socks_prefer_no_auth; + } + if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + newconn->port = 0; + newconn->address = tor_strdup(conn->address); + log_notice(LD_NET, "New SOCKS SocksSocket connection opened"); TO_ENTRY_CONN(newconn)->socks_request->socks_prefer_no_auth = TO_LISTENER_CONN(conn)->socks_prefer_no_auth; } @@ -1428,7 +1571,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) fmt_and_decorate_addr(&addr)); } - } else if (conn->socket_family == AF_UNIX) { + } else if (conn->socket_family == AF_UNIX && conn->type != CONN_TYPE_AP) { /* For now only control ports can be Unix domain sockets * and listeners at the same time */ tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); @@ -2392,6 +2535,7 @@ connection_is_rate_limited(connection_t *conn) return 0; /* Internal connection */ else if (! options->CountPrivateBandwidth && (tor_addr_family(&conn->addr) == AF_UNSPEC || /* no address */ + tor_addr_family(&conn->addr) == AF_UNIX || /* no address */ tor_addr_is_internal(&conn->addr, 0))) return 0; /* Internal address */ else diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index d8f397bd90..390ad07b83 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1233,7 +1233,8 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, { tor_addr_t addr; /* XXX Duplicate call to tor_addr_parse. */ - if (tor_addr_parse(&addr, socks->address) >= 0) { + if (tor_addr_parse(&addr, socks->address) >= 0 && + !conn->is_socks_socket) { sa_family_t family = tor_addr_family(&addr); if ((family == AF_INET && ! conn->ipv4_traffic_ok) || (family == AF_INET6 && ! conn->ipv4_traffic_ok)) { @@ -1836,6 +1837,9 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn) if (!ap_conn->ipv4_traffic_ok) flags |= BEGIN_FLAG_IPV4_NOT_OK; + if (ap_conn->is_socks_socket) + return 0; + exitnode = node_get_by_id(cpath_layer->extend_info->identity_digest); if (ap_conn->ipv6_traffic_ok && exitnode) { diff --git a/src/or/main.c b/src/or/main.c index c925b7d593..0c49b40314 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -385,6 +385,10 @@ connection_remove(connection_t *conn) (int)conn->s, conn_type_to_string(conn->type), smartlist_len(connection_array)); + if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { + log_notice(LD_NET, "Closing SOCKS SocksSocket connection"); + } + control_event_conn_bandwidth(conn); tor_assert(conn->conn_array_index >= 0); diff --git a/src/or/or.h b/src/or/or.h index 58e2164665..5bb5f5bf71 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1702,6 +1702,9 @@ typedef struct entry_connection_t { * do we prefer IPv6? */ unsigned int prefer_ipv6_virtaddr : 1; + /** Are we a socks SocksSocket listener? */ + unsigned int is_socks_socket:1; + } entry_connection_t; typedef enum { @@ -3528,6 +3531,10 @@ typedef struct { * for control connections. */ int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ + config_line_t *SocksSocket; /**< List of Unix Domain Sockets to listen on + * for SOCKS connections. */ + + int SocksSocketsGroupWritable; /**< Boolean: Are SOCKS sockets g+rw? */ /** Ports to listen on for directory connections. */ config_line_t *DirPort_lines; config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */ @@ -3550,6 +3557,7 @@ typedef struct { */ unsigned int ORPort_set : 1; unsigned int SocksPort_set : 1; + unsigned int SocksSocket_set : 1; unsigned int TransPort_set : 1; unsigned int NATDPort_set : 1; unsigned int ControlPort_set : 1; diff --git a/src/or/relay.c b/src/or/relay.c index 2d11096309..b7ab8081e9 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1327,8 +1327,9 @@ connection_edge_process_relay_cell_not_open( return 0; } - if ((family == AF_INET && ! entry_conn->ipv4_traffic_ok) || - (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) { + if (((family == AF_INET && ! entry_conn->ipv4_traffic_ok) || + (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) && + (!entry_conn->is_socks_socket)) { log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a connected cell to %s with unsupported address family." " Closing.", fmt_addr(&addr)); From c6451e4c9f2e36f447b4da7d91403a16dde4d2e7 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 7 Jan 2015 19:17:04 +0000 Subject: [PATCH 02/12] Refactor check_location_for_unix_socket()/check_location_for_socks_unix_socket() to eliminate duplicated code --- src/or/connection.c | 104 ++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 46 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index ad7e6de9b6..bb2cff338f 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -918,54 +918,57 @@ warn_too_many_conns(void) } #ifdef HAVE_SYS_UN_H -/** Check whether we should be willing to open an AF_UNIX socket in - * path. Return 0 if we should go ahead and -1 if we shouldn't. */ + +#define UNIX_SOCKET_PURPOSE_CONTROL_SOCKET 0 +#define UNIX_SOCKET_PURPOSE_SOCKS_SOCKET 1 + +/** Check if the purpose isn't one of the ones we know what to do with */ + static int -check_location_for_unix_socket(const or_options_t *options, const char *path) +is_valid_unix_socket_purpose(int purpose) { - int r = -1; - char *p = tor_strdup(path); - cpd_check_t flags = CPD_CHECK_MODE_ONLY; - if (get_parent_directory(p)<0 || p[0] != '/') { - log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " - "relative paths for unix sockets.", path); - goto done; + int valid = 0; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + valid = 1; + break; } - if (options->ControlSocketsGroupWritable) - flags |= CPD_GROUP_OK; - - if (check_private_dir(p, flags, options->User) < 0) { - char *escpath, *escdir; - escpath = esc_for_log(path); - escdir = esc_for_log(p); - log_warn(LD_GENERAL, "Before Tor can create a control socket in %s, the " - "directory %s needs to exist, and to be accessible only by the " - "user%s account that is running Tor. (On some Unix systems, " - "anybody who can list a socket can connect to it, so Tor is " - "being careful.)", escpath, escdir, - options->ControlSocketsGroupWritable ? " and group" : ""); - tor_free(escpath); - tor_free(escdir); - goto done; - } - - r = 0; - done: - tor_free(p); - return r; + return valid; +} + +/** Return a string description of a unix socket purpose */ +static const char * +unix_socket_purpose_to_string(int purpose) +{ + const char *s = "unknown-purpose socket"; + + switch (purpose) { + case UNIX_SOCKET_PURPOSE_CONTROL_SOCKET: + s = "control socket"; + break; + case UNIX_SOCKET_PURPOSE_SOCKS_SOCKET: + s = "SOCKS socket"; + break; + } + + return s; } -#endif -#ifdef HAVE_SYS_UN_H /** Check whether we should be willing to open an AF_UNIX socket in * path. Return 0 if we should go ahead and -1 if we shouldn't. */ static int -check_location_for_socks_unix_socket(const or_options_t *options, - const char *path) +check_location_for_unix_socket(const or_options_t *options, const char *path, + int purpose) { int r = -1; - char *p = tor_strdup(path); + char *p = NULL; + + tor_assert(is_valid_unix_socket_purpose(purpose)); + + p = tor_strdup(path); cpd_check_t flags = CPD_CHECK_MODE_ONLY; if (get_parent_directory(p)<0 || p[0] != '/') { log_warn(LD_GENERAL, "Bad unix socket address '%s'. Tor does not support " @@ -973,19 +976,24 @@ check_location_for_socks_unix_socket(const or_options_t *options, goto done; } - if (options->SocksSocketsGroupWritable) + if ((purpose == UNIX_SOCKET_PURPOSE_CONTROL_SOCKET && + options->ControlSocketsGroupWritable) || + (purpose == UNIX_SOCKET_PURPOSE_SOCKS_SOCKET && + options->SocksSocketsGroupWritable)) { flags |= CPD_GROUP_OK; + } if (check_private_dir(p, flags, options->User) < 0) { char *escpath, *escdir; escpath = esc_for_log(path); escdir = esc_for_log(p); - log_warn(LD_GENERAL, "Before Tor can create a SocksSocket in %s, the " - "directory %s needs to exist, and to be accessible only by the " - "user%s account that is running Tor. (On some Unix systems, " - "anybody who can list a socket can connect to it, so Tor is " - "being careful.)", escpath, escdir, - options->SocksSocketsGroupWritable ? " and group" : ""); + log_warn(LD_GENERAL, "Before Tor can create a %s in %s, the directory " + "%s needs to exist, and to be accessible only by the user%s " + "account that is running Tor. (On some Unix systems, anybody " + "who can list a socket can connect to it, so Tor is being " + "careful.)", + unix_socket_purpose_to_string(purpose), escpath, escdir, + options->ControlSocketsGroupWritable ? " and group" : ""); tor_free(escpath); tor_free(escdir); goto done; @@ -1109,8 +1117,10 @@ connection_listener_new(const struct sockaddr *listensockaddr, type != CONN_TYPE_CONTROL_LISTENER) { tor_assert(listensockaddr->sa_family == AF_UNIX); - if (check_location_for_socks_unix_socket(options, address) < 0) + if (check_location_for_unix_socket(options, address, + UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { goto err; + } log_notice(LD_NET, "Opening SocksSocket %s on %s", conn_type_to_string(type), address); @@ -1251,8 +1261,10 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_assert(type == CONN_TYPE_CONTROL_LISTENER); if ( type == CONN_TYPE_CONTROL_LISTENER ) { - if (check_location_for_unix_socket(options, address) < 0) + if (check_location_for_unix_socket(options, address, + UNIX_SOCKET_PURPOSE_CONTROL_SOCKET) < 0) { goto err; + } } log_notice(LD_NET, "Opening %s on %s", From 48633c07660216d3b852b609c44fa318d55908f0 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 7 Jan 2015 19:45:59 +0000 Subject: [PATCH 03/12] Rename is_tcp in connection_listener_new(), since AF_UNIX means SOCK_STREAM no longer implies TCP --- src/or/connection.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index bb2cff338f..c17a414319 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1084,8 +1084,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, listensockaddr->sa_family == AF_INET6 || (listensockaddr->sa_family == AF_UNIX && type != CONN_TYPE_CONTROL_LISTENER)) { - int is_tcp = (type != CONN_TYPE_AP_DNS_LISTENER); - if (is_tcp) + int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER); + if (is_stream) start_reading = 1; if ( listensockaddr->sa_family == AF_INET || @@ -1097,8 +1097,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, conn_type_to_string(type), fmt_addrport(&addr, usePort)); s = tor_open_socket_nonblocking(tor_addr_family(&addr), - is_tcp ? SOCK_STREAM : SOCK_DGRAM, - is_tcp ? IPPROTO_TCP: IPPROTO_UDP); + is_stream ? SOCK_STREAM : SOCK_DGRAM, + is_stream ? IPPROTO_TCP: IPPROTO_UDP); if (!SOCKET_OK(s)) { log_warn(LD_NET, "Socket creation failed: %s", tor_socket_strerror(tor_socket_errno(-1))); @@ -1228,7 +1228,7 @@ connection_listener_new(const struct sockaddr *listensockaddr, goto err; } - if (is_tcp) { + if (is_stream) { if (tor_listen(s) < 0) { log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, tor_socket_strerror(tor_socket_errno(s))); From 2ca1c386b0d1c396fa8d8f4b5334349e24a2f9e8 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 7 Jan 2015 22:51:24 +0000 Subject: [PATCH 04/12] Bring sanity to connection_listener_new() --- src/or/connection.c | 202 +++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 135 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index c17a414319..51d891d63a 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -965,7 +965,7 @@ check_location_for_unix_socket(const or_options_t *options, const char *path, { int r = -1; char *p = NULL; - + tor_assert(is_valid_unix_socket_purpose(purpose)); p = tor_strdup(path); @@ -1081,105 +1081,30 @@ connection_listener_new(const struct sockaddr *listensockaddr, } if (listensockaddr->sa_family == AF_INET || - listensockaddr->sa_family == AF_INET6 || - (listensockaddr->sa_family == AF_UNIX && - type != CONN_TYPE_CONTROL_LISTENER)) { + listensockaddr->sa_family == AF_INET6) { int is_stream = (type != CONN_TYPE_AP_DNS_LISTENER); if (is_stream) start_reading = 1; - if ( listensockaddr->sa_family == AF_INET || - listensockaddr->sa_family == AF_INET6) { + tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); - tor_addr_from_sockaddr(&addr, listensockaddr, &usePort); + log_notice(LD_NET, "Opening %s on %s", + conn_type_to_string(type), fmt_addrport(&addr, usePort)); - log_notice(LD_NET, "Opening %s on %s", - conn_type_to_string(type), fmt_addrport(&addr, usePort)); - - s = tor_open_socket_nonblocking(tor_addr_family(&addr), - is_stream ? SOCK_STREAM : SOCK_DGRAM, - is_stream ? IPPROTO_TCP: IPPROTO_UDP); - if (!SOCKET_OK(s)) { - log_warn(LD_NET, "Socket creation failed: %s", - tor_socket_strerror(tor_socket_errno(-1))); - goto err; - } - - if (make_socket_reuseable(s) < 0) { - log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", - conn_type_to_string(type), - tor_socket_strerror(errno)); - } + s = tor_open_socket_nonblocking(tor_addr_family(&addr), + is_stream ? SOCK_STREAM : SOCK_DGRAM, + is_stream ? IPPROTO_TCP: IPPROTO_UDP); + if (!SOCKET_OK(s)) { + log_warn(LD_NET, "Socket creation failed: %s", + tor_socket_strerror(tor_socket_errno(-1))); + goto err; } -#ifdef HAVE_SYS_UN_H - if (listensockaddr->sa_family == AF_UNIX && - type != CONN_TYPE_CONTROL_LISTENER) { - tor_assert(listensockaddr->sa_family == AF_UNIX); - - if (check_location_for_unix_socket(options, address, - UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { - goto err; - } - - log_notice(LD_NET, "Opening SocksSocket %s on %s", - conn_type_to_string(type), address); - - tor_addr_make_unspec(&addr); - - if (unlink(address) < 0 && errno != ENOENT) { - log_warn(LD_NET, "Could not unlink %s: %s", address, - strerror(errno)); - goto err; - } - - s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); - if (! SOCKET_OK(s)) { - log_warn(LD_NET, "SocksSocket socket creation failed: %s.", - strerror(errno)); - goto err; - } - - if (bind(s, listensockaddr, - (socklen_t)sizeof(struct sockaddr_un)) == -1) { - log_warn(LD_NET, "Bind to %s failed: %s.", address, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } -#ifdef HAVE_PWD_H - if (options->User) { - pw = getpwnam(options->User); - if (pw == NULL) { - log_warn(LD_NET, - "Unable to chown() %s socket: user %s not found.", - address, options->User); - goto err; - } else if (chown(address, pw->pw_uid, pw->pw_gid) < 0) { - log_warn(LD_NET, "Unable to chown() %s socket: %s.", - address, strerror(errno)); - goto err; - } - } -#endif - - if (options->SocksSocketsGroupWritable) { - /* We need to use chmod; fchmod doesn't work on sockets on all - * platforms. */ - if (chmod(address, 0660) < 0) { - log_warn(LD_FS, "Unable to make %s group-writable.", address); - goto err; - } - } - - if (listen(s, SOMAXCONN) < 0) { - log_warn(LD_NET, "Could not listen on %s: %s", address, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } + if (make_socket_reuseable(s) < 0) { + log_warn(LD_NET, "Error setting SO_REUSEADDR flag on %s: %s", + conn_type_to_string(type), + tor_socket_strerror(errno)); } -#else - (void)options; -#endif /* HAVE_SYS_UN_H */ #if defined USE_TRANSPARENT && defined(IP_TRANSPARENT) if (options->TransProxyType_parsed == TPT_TPROXY && @@ -1217,54 +1142,57 @@ connection_listener_new(const struct sockaddr *listensockaddr, } #endif - if (listensockaddr->sa_family != AF_UNIX) { - if (bind(s,listensockaddr,socklen) < 0) { - const char *helpfulhint = ""; - int e = tor_socket_errno(s); - if (ERRNO_IS_EADDRINUSE(e)) - helpfulhint = ". Is Tor already running?"; - log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, - tor_socket_strerror(e), helpfulhint); + if (bind(s,listensockaddr,socklen) < 0) { + const char *helpfulhint = ""; + int e = tor_socket_errno(s); + if (ERRNO_IS_EADDRINUSE(e)) + helpfulhint = ". Is Tor already running?"; + log_warn(LD_NET, "Could not bind to %s:%u: %s%s", address, usePort, + tor_socket_strerror(e), helpfulhint); + goto err; + } + + if (is_stream) { + if (tor_listen(s) < 0) { + log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, + tor_socket_strerror(tor_socket_errno(s))); goto err; } - - if (is_stream) { - if (tor_listen(s) < 0) { - log_warn(LD_NET, "Could not listen on %s:%u: %s", address, usePort, - tor_socket_strerror(tor_socket_errno(s))); - goto err; - } - } - - if (usePort != 0) { - gotPort = usePort; - } else { - tor_addr_t addr2; - struct sockaddr_storage ss; - socklen_t ss_len=sizeof(ss); - if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { - log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", - conn_type_to_string(type), - tor_socket_strerror(tor_socket_errno(s))); - gotPort = 0; - } - tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); - } + } + + if (usePort != 0) { + gotPort = usePort; + } else { + tor_addr_t addr2; + struct sockaddr_storage ss; + socklen_t ss_len=sizeof(ss); + if (getsockname(s, (struct sockaddr*)&ss, &ss_len)<0) { + log_warn(LD_NET, "getsockname() couldn't learn address for %s: %s", + conn_type_to_string(type), + tor_socket_strerror(tor_socket_errno(s))); + gotPort = 0; + } + tor_addr_from_sockaddr(&addr2, (struct sockaddr*)&ss, &gotPort); } #ifdef HAVE_SYS_UN_H - } else if (listensockaddr->sa_family == AF_UNIX && - type != CONN_TYPE_AP_LISTENER) { + /* + * AF_UNIX generic setup stuff (this covers both CONN_TYPE_CONTROL_LISTENER + * and CONN_TYPE_AP_LISTENER cases) + */ + } else if (listensockaddr->sa_family == AF_UNIX) { + /* We want to start reading for both AF_UNIX cases */ start_reading = 1; - /* For now only control ports can be Unix domain sockets + /* For now only control ports or SOCKS ports can be Unix domain sockets * and listeners at the same time */ - tor_assert(type == CONN_TYPE_CONTROL_LISTENER); + tor_assert(type == CONN_TYPE_CONTROL_LISTENER || + type == CONN_TYPE_AP_LISTENER); - if ( type == CONN_TYPE_CONTROL_LISTENER ) { - if (check_location_for_unix_socket(options, address, - UNIX_SOCKET_PURPOSE_CONTROL_SOCKET) < 0) { + if (check_location_for_unix_socket(options, address, + (type == CONN_TYPE_CONTROL_LISTENER) ? + UNIX_SOCKET_PURPOSE_CONTROL_SOCKET : + UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { goto err; - } } log_notice(LD_NET, "Opening %s on %s", @@ -1277,17 +1205,20 @@ connection_listener_new(const struct sockaddr *listensockaddr, strerror(errno)); goto err; } + s = tor_open_socket_nonblocking(AF_UNIX, SOCK_STREAM, 0); if (! SOCKET_OK(s)) { log_warn(LD_NET,"Socket creation failed: %s.", strerror(errno)); goto err; } - if (bind(s, listensockaddr, (socklen_t)sizeof(struct sockaddr_un)) == -1) { + if (bind(s, listensockaddr, + (socklen_t)sizeof(struct sockaddr_un)) == -1) { log_warn(LD_NET,"Bind to %s failed: %s.", address, tor_socket_strerror(tor_socket_errno(s))); goto err; } + #ifdef HAVE_PWD_H if (options->User) { pw = tor_getpwnam(options->User); @@ -1302,7 +1233,9 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } #endif - if (options->ControlSocketsGroupWritable) { + + if (type == CONN_TYPE_CONTROL_LISTENER && + options->ControlSocketsGroupWritable) { /* We need to use chmod; fchmod doesn't work on sockets on all * platforms. */ if (chmod(address, 0660) < 0) { @@ -1311,7 +1244,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, } } - if (options->SocksSocketsGroupWritable) { + if (type == CONN_TYPE_AP_LISTENER && + options->SocksSocketsGroupWritable) { /* We need to use chmod; fchmod doesn't work on sockets on all * platforms. */ if (chmod(address, 0660) < 0) { @@ -1325,8 +1259,6 @@ connection_listener_new(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } -#else - (void)options; #endif /* HAVE_SYS_UN_H */ } else { log_err(LD_BUG, "Got unexpected address family %d.", From a3bcde3638d035303a9a5bf8373c1b841a1f5636 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Wed, 7 Jan 2015 22:57:51 +0000 Subject: [PATCH 05/12] Downgrade open/close log message for SocksSocket --- src/or/connection.c | 2 +- src/or/main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index 51d891d63a..c78cebad70 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1506,7 +1506,7 @@ connection_handle_listener_read(connection_t *conn, int new_type) if (new_type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { newconn->port = 0; newconn->address = tor_strdup(conn->address); - log_notice(LD_NET, "New SOCKS SocksSocket connection opened"); + log_info(LD_NET, "New SOCKS SocksSocket connection opened"); TO_ENTRY_CONN(newconn)->socks_request->socks_prefer_no_auth = TO_LISTENER_CONN(conn)->socks_prefer_no_auth; } diff --git a/src/or/main.c b/src/or/main.c index 0c49b40314..233b444791 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -386,7 +386,7 @@ connection_remove(connection_t *conn) smartlist_len(connection_array)); if (conn->type == CONN_TYPE_AP && conn->socket_family == AF_UNIX) { - log_notice(LD_NET, "Closing SOCKS SocksSocket connection"); + log_info(LD_NET, "Closing SOCKS SocksSocket connection"); } control_event_conn_bandwidth(conn); From 0729b2be535df8a114c6c219e90aa3258caefcdb Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 9 Jan 2015 20:49:54 +0000 Subject: [PATCH 06/12] Add support for a default list of paths and passing '0' to disable it to parse_unix_socket_config() --- src/or/config.c | 58 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index e080e7749b..87bf834dd5 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -6021,22 +6021,66 @@ parse_port_config(smartlist_t *out, /** Parse a list of config_line_t for an AF_UNIX unix socket listener option * from cfg and add them to out. No fancy options are - * supported: the line contains nothing but the path to the AF_UNIX socket. */ + * supported: the line contains nothing but the path to the AF_UNIX socket. + * We support a *Socket 0 syntax to explicitly disable if we enable by + * default. To use this, pass an initial list containing the default + * paths into this function, and if the list passed in is nonempty and + * contains paths they will replace it. If it contains only '0' the + * initial list will be cleared. */ static int parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, int listener_type) { + /* We can say things like SocksSocket 0 or ControlSocket 0 to explicitly + * disable this feature; use this to track if we've seen a disable line + */ + + int unix_socket_disable = 0, initial_len; + size_t len; if (!out) return 0; + /* + * Keep track of this so we know if we added things later; it's + * invalid to say both *Socket 0 to disable and list paths + */ + initial_len = smartlist_len(out); + for ( ; cfg; cfg = cfg->next) { - size_t len = strlen(cfg->value); - port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); - port->is_unix_addr = 1; - memcpy(port->unix_addr, cfg->value, len+1); - port->type = listener_type; - smartlist_add(out, port); + if (strcmp(cfg->value, "0") != 0) { + /* Clear the default list if we have one */ + if (initial_len > 0) { + SMARTLIST_FOREACH(out, port_cfg_t *, port, tor_free(port)); + smartlist_clear(out); + initial_len = 0; + } + /* Now add it */ + len = strlen(cfg->value); + port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); + port->is_unix_addr = 1; + memcpy(port->unix_addr, cfg->value, len+1); + port->type = listener_type; + smartlist_add(out, port); + } else { + /* Keep track that we've seen a disable */ + unix_socket_disable = 1; + } + } + + if (unix_socket_disable) { + if (initial_len == 0 && smartlist_len(out) > 0) { + /* We saw a disable line and a path; bad news */ + return -1; + } else if (initial_len > 0) { + /* + * We had a non-empty default list, and still have it, so we have + * to clear it. + */ + SMARTLIST_FOREACH(out, port_cfg_t *, port, tor_free(port)); + smartlist_clear(out); + initial_len = 0; + } } return 0; From 78956f5d857f573106fd4dd4d2f9220af32be1b3 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Fri, 9 Jan 2015 20:54:59 +0000 Subject: [PATCH 07/12] Document disable option for ControlSocket and SocksSocket --- doc/tor.1.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index 43aade9d9e..7f280d105c 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -294,7 +294,7 @@ GENERAL OPTIONS [[ControlSocket]] **ControlSocket** __Path__:: Like ControlPort, but listens on a Unix domain socket, rather than a TCP - socket. (Unix and Unix-like systems only.) + socket. '0' disables ControlSocket (Unix and Unix-like systems only.) [[ControlSocketsGroupWritable]] **ControlSocketsGroupWritable** **0**|**1**:: If this option is set to 0, don't allow the filesystem group to read and @@ -485,7 +485,7 @@ GENERAL OPTIONS [[SocksSocket]] **SocksSocket** __Path__:: Like SocksPort, but listens on a Unix domain socket, rather than a TCP - socket. (Unix and Unix-like systems only.) + socket. '0' disables SocksSocket (Unix and Unix-like systems only.) [[SocksSocketsGroupWritable]] **SocksSocketsGroupWritable** **0**|**1**:: If this option is set to 0, don't allow the filesystem group to read and From 62f297fff062afa5aabfb5cd5152897a1ca4591b Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 12 Jan 2015 16:26:34 +0000 Subject: [PATCH 08/12] Kill duplicated code in connection_listener_new() --- src/or/connection.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index c78cebad70..9866c4c804 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1234,18 +1234,10 @@ connection_listener_new(const struct sockaddr *listensockaddr, } #endif - if (type == CONN_TYPE_CONTROL_LISTENER && - options->ControlSocketsGroupWritable) { - /* We need to use chmod; fchmod doesn't work on sockets on all - * platforms. */ - if (chmod(address, 0660) < 0) { - log_warn(LD_FS,"Unable to make %s group-writable.", address); - goto err; - } - } - - if (type == CONN_TYPE_AP_LISTENER && - options->SocksSocketsGroupWritable) { + if ((type == CONN_TYPE_CONTROL_LISTENER && + options->ControlSocketsGroupWritable) || + (type == CONN_TYPE_AP_LISTENER && + options->SocksSocketsGroupWritable)) { /* We need to use chmod; fchmod doesn't work on sockets on all * platforms. */ if (chmod(address, 0660) < 0) { From f50068b17e77f9749fe798ef46124baa811d5b0a Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Mon, 12 Jan 2015 22:12:18 +0000 Subject: [PATCH 09/12] Fix default list handling for parse_unix_socket_config(); avoid clearing whole pre-existing list --- src/or/config.c | 70 +++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 87bf834dd5..aea0498712 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -6023,45 +6023,37 @@ parse_port_config(smartlist_t *out, * from cfg and add them to out. No fancy options are * supported: the line contains nothing but the path to the AF_UNIX socket. * We support a *Socket 0 syntax to explicitly disable if we enable by - * default. To use this, pass an initial list containing the default - * paths into this function, and if the list passed in is nonempty and - * contains paths they will replace it. If it contains only '0' the - * initial list will be cleared. */ + * default. To use this, pass a non-NULL list containing the default + * paths into this function as the 2nd parameter, and if no config lines at all + * are present they will be added to the output list. If the only config line + * present is '0' the input list will be unmodified. + */ static int -parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, - int listener_type) +parse_unix_socket_config(smartlist_t *out, smartlist_t *defaults, + const config_line_t *cfg, int listener_type) { /* We can say things like SocksSocket 0 or ControlSocket 0 to explicitly * disable this feature; use this to track if we've seen a disable line */ - int unix_socket_disable = 0, initial_len; + int unix_socket_disable = 0; size_t len; + smartlist_t *ports_to_add = NULL; if (!out) return 0; - /* - * Keep track of this so we know if we added things later; it's - * invalid to say both *Socket 0 to disable and list paths - */ - initial_len = smartlist_len(out); + ports_to_add = smartlist_new(); for ( ; cfg; cfg = cfg->next) { if (strcmp(cfg->value, "0") != 0) { - /* Clear the default list if we have one */ - if (initial_len > 0) { - SMARTLIST_FOREACH(out, port_cfg_t *, port, tor_free(port)); - smartlist_clear(out); - initial_len = 0; - } - /* Now add it */ + /* We have a non-disable; add it */ len = strlen(cfg->value); port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t) + len + 1); port->is_unix_addr = 1; memcpy(port->unix_addr, cfg->value, len+1); port->type = listener_type; - smartlist_add(out, port); + smartlist_add(ports_to_add, port); } else { /* Keep track that we've seen a disable */ unix_socket_disable = 1; @@ -6069,18 +6061,33 @@ parse_unix_socket_config(smartlist_t *out, const config_line_t *cfg, } if (unix_socket_disable) { - if (initial_len == 0 && smartlist_len(out) > 0) { + if (smartlist_len(ports_to_add) > 0) { /* We saw a disable line and a path; bad news */ + SMARTLIST_FOREACH(ports_to_add, port_cfg_t *, port, tor_free(port)); + smartlist_free(ports_to_add); return -1; - } else if (initial_len > 0) { - /* - * We had a non-empty default list, and still have it, so we have - * to clear it. - */ - SMARTLIST_FOREACH(out, port_cfg_t *, port, tor_free(port)); - smartlist_clear(out); - initial_len = 0; } + /* else we have a disable and nothing else, so add nothing to out */ + } else { + /* No disable; do we have any ports to add that we parsed? */ + if (smartlist_len(ports_to_add) > 0) { + SMARTLIST_FOREACH_BEGIN(ports_to_add, port_cfg_t *, port) { + smartlist_add(out, port); + } SMARTLIST_FOREACH_END(port); + } else if (defaults != NULL && smartlist_len(defaults) > 0) { + /* No, but we have some defaults to copy */ + SMARTLIST_FOREACH_BEGIN(defaults, const port_cfg_t *, defport) { + tor_assert(defport->is_unix_addr); + tor_assert(defport->unix_addr); + len = sizeof(port_cfg_t) + strlen(defport->unix_addr) + 1; + port_cfg_t *port = tor_malloc_zero(len); + memcpy(port, defport, len); + smartlist_add(out, port); + } SMARTLIST_FOREACH_END(defport); + } + + /* Free the temporary smartlist we used */ + smartlist_free(ports_to_add); } return 0; @@ -6174,13 +6181,14 @@ parse_ports(or_options_t *options, int validate_only, "configuration"); goto err; } - if (parse_unix_socket_config(ports, + + if (parse_unix_socket_config(ports, NULL, options->ControlSocket, CONN_TYPE_CONTROL_LISTENER) < 0) { *msg = tor_strdup("Invalid ControlSocket configuration"); goto err; } - if (parse_unix_socket_config(ports, + if (parse_unix_socket_config(ports, NULL, options->SocksSocket, CONN_TYPE_AP_LISTENER) < 0) { *msg = tor_strdup("Invalid SocksSocket configuration"); From cb047f40786bf87f7fb17a500ca35eca9ba34f7c Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 13 Jan 2015 00:18:17 +0000 Subject: [PATCH 10/12] Fix ipv4/ipv6 traffic bits on AF_UNIX socks listeners and remove hacky workarounds for brokenness --- src/or/config.c | 14 ++++++++++++++ src/or/connection_edge.c | 6 +----- src/or/relay.c | 3 +-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index aea0498712..219200f799 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -6053,6 +6053,20 @@ parse_unix_socket_config(smartlist_t *out, smartlist_t *defaults, port->is_unix_addr = 1; memcpy(port->unix_addr, cfg->value, len+1); port->type = listener_type; + if (listener_type == CONN_TYPE_AP_LISTENER) { + /* Some more bits to twiddle for this case + * + * XXX this should support parsing the same options + * parse_port_config() does, and probably that code should be + * factored out into a function we can call from here. For + * now, some reasonable defaults. + */ + + port->ipv4_traffic = 1; + port->ipv6_traffic = 1; + port->cache_ipv4_answers = 1; + port->cache_ipv6_answers = 1; + } smartlist_add(ports_to_add, port); } else { /* Keep track that we've seen a disable */ diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 390ad07b83..d8f397bd90 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1233,8 +1233,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, { tor_addr_t addr; /* XXX Duplicate call to tor_addr_parse. */ - if (tor_addr_parse(&addr, socks->address) >= 0 && - !conn->is_socks_socket) { + if (tor_addr_parse(&addr, socks->address) >= 0) { sa_family_t family = tor_addr_family(&addr); if ((family == AF_INET && ! conn->ipv4_traffic_ok) || (family == AF_INET6 && ! conn->ipv4_traffic_ok)) { @@ -1837,9 +1836,6 @@ connection_ap_get_begincell_flags(entry_connection_t *ap_conn) if (!ap_conn->ipv4_traffic_ok) flags |= BEGIN_FLAG_IPV4_NOT_OK; - if (ap_conn->is_socks_socket) - return 0; - exitnode = node_get_by_id(cpath_layer->extend_info->identity_digest); if (ap_conn->ipv6_traffic_ok && exitnode) { diff --git a/src/or/relay.c b/src/or/relay.c index b7ab8081e9..ac36e72b3b 100644 --- a/src/or/relay.c +++ b/src/or/relay.c @@ -1328,8 +1328,7 @@ connection_edge_process_relay_cell_not_open( } if (((family == AF_INET && ! entry_conn->ipv4_traffic_ok) || - (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok)) && - (!entry_conn->is_socks_socket)) { + (family == AF_INET6 && ! entry_conn->ipv6_traffic_ok))) { log_fn(LOG_PROTOCOL_WARN, LD_APP, "Got a connected cell to %s with unsupported address family." " Closing.", fmt_addr(&addr)); From 4316bb601a95ebe6e8353cc0a795605889039fe3 Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 13 Jan 2015 00:21:59 +0000 Subject: [PATCH 11/12] Remove no-longer-accurate comment from connection.c --- src/or/connection.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/or/connection.c b/src/or/connection.c index 9866c4c804..0a7a6a882b 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1508,8 +1508,6 @@ connection_handle_listener_read(connection_t *conn, int new_type) } } else if (conn->socket_family == AF_UNIX && conn->type != CONN_TYPE_AP) { - /* For now only control ports can be Unix domain sockets - * and listeners at the same time */ tor_assert(conn->type == CONN_TYPE_CONTROL_LISTENER); tor_assert(new_type == CONN_TYPE_CONTROL); log_notice(LD_CONTROL, "New control connection opened."); From 066acaf6b9e5c38fc392e85c14457f338d3c1dff Mon Sep 17 00:00:00 2001 From: Andrea Shepard Date: Tue, 13 Jan 2015 00:27:04 +0000 Subject: [PATCH 12/12] Explicitly chmod AF_UNIX sockets to 0600 when *GroupWritable isn't specified --- src/or/connection.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/or/connection.c b/src/or/connection.c index 0a7a6a882b..a3c7019812 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -1244,6 +1244,16 @@ connection_listener_new(const struct sockaddr *listensockaddr, log_warn(LD_FS,"Unable to make %s group-writable.", address); goto err; } + } else if ((type == CONN_TYPE_CONTROL_LISTENER && + !(options->ControlSocketsGroupWritable)) || + (type == CONN_TYPE_AP_LISTENER && + !(options->SocksSocketsGroupWritable))) { + /* We need to use chmod; fchmod doesn't work on sockets on all + * platforms. */ + if (chmod(address, 0600) < 0) { + log_warn(LD_FS,"Unable to make %s group-writable.", address); + goto err; + } } if (listen(s, SOMAXCONN) < 0) {