Allow {World,Group}Writable on AF_UNIX {Socks,Control}Ports.

Closes ticket 15220
This commit is contained in:
Nick Mathewson 2015-03-11 13:26:14 -04:00
parent 985687bc4f
commit 809517a863
5 changed files with 88 additions and 37 deletions

5
changes/feature15220 Normal file
View File

@ -0,0 +1,5 @@
o Minor features (client, unix sockets):
- Add GroupWritable and WorldWritable options to unix-socket based
SocksPort and ControlPort options. These options apply to a single
socket, and override {Control,Socks}SocketsGroupWritable. Closes
ticket 15220.

View File

@ -274,7 +274,7 @@ GENERAL OPTIONS
all sockets will be set to this limit. Must be a value between 2048 and all sockets will be set to this limit. Must be a value between 2048 and
262144, in 1024 byte increments. Default of 8192 is recommended. 262144, in 1024 byte increments. Default of 8192 is recommended.
[[ControlPort]] **ControlPort** __PORT__|**unix:**__path__|**auto**:: [[ControlPort]] **ControlPort** __PORT__|**unix:**__path__|**auto** [__flags__]::
If set, Tor will accept connections on this port and allow those If set, Tor will accept connections on this port and allow those
connections to control the Tor process using the Tor Control Protocol connections to control the Tor process using the Tor Control Protocol
(described in control-spec.txt). Note: unless you also specify one or (described in control-spec.txt). Note: unless you also specify one or
@ -284,6 +284,14 @@ GENERAL OPTIONS
method is sufficient to authenticate to Tor.) This method is sufficient to authenticate to Tor.) This
option is required for many Tor controllers; most use the value of 9051. option is required for many Tor controllers; most use the value of 9051.
Set it to "auto" to have Tor pick a port for you. (Default: 0) Set it to "auto" to have Tor pick a port for you. (Default: 0)
+
Recognized flags are::
**GroupWritable**;;
Unix domain sockets only: makes the socket get created as
group-writable.
**WorldWritable**;;
Unix domain sockets only: makes the socket get created as
world-writable.
[[ControlListenAddress]] **ControlListenAddress** __IP__[:__PORT__]:: [[ControlListenAddress]] **ControlListenAddress** __IP__[:__PORT__]::
Bind the controller listener to this address. If you specify a port, bind Bind the controller listener to this address. If you specify a port, bind
@ -1015,6 +1023,12 @@ The following options are useful only for clients (that is, if
**CacheIPv6DNS**;; **CacheIPv6DNS**;;
Tells the client to remember IPv6 DNS answers we receive from exit Tells the client to remember IPv6 DNS answers we receive from exit
nodes via this connection. nodes via this connection.
**GroupWritable**;;
Unix domain sockets only: makes the socket get created as
group-writable.
**WorldWritable**;;
Unix domain sockets only: makes the socket get created as
world-writable.
**CacheDNS**;; **CacheDNS**;;
Tells the client to remember all DNS answers we receive from exit Tells the client to remember all DNS answers we receive from exit
nodes via this connection. nodes via this connection.

View File

@ -5618,7 +5618,7 @@ warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
* then emit a stronger warning and remove the port from the list. * then emit a stronger warning and remove the port from the list.
*/ */
static void static void
warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid) warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid_nonlocal)
{ {
int warned = 0; int warned = 0;
SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) { SMARTLIST_FOREACH_BEGIN(ports, port_cfg_t *, port) {
@ -5627,7 +5627,7 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
if (port->is_unix_addr) if (port->is_unix_addr)
continue; continue;
if (!tor_addr_is_loopback(&port->addr)) { if (!tor_addr_is_loopback(&port->addr)) {
if (forbid) { if (forbid_nonlocal) {
if (!warned) if (!warned)
log_warn(LD_CONFIG, log_warn(LD_CONFIG,
"You have a ControlPort set to accept " "You have a ControlPort set to accept "
@ -5655,13 +5655,14 @@ warn_nonlocal_controller_ports(smartlist_t *ports, unsigned forbid)
} SMARTLIST_FOREACH_END(port); } SMARTLIST_FOREACH_END(port);
} }
#define CL_PORT_NO_OPTIONS (1u<<0) #define CL_PORT_NO_STREAM_OPTIONS (1u<<0)
#define CL_PORT_WARN_NONLOCAL (1u<<1) #define CL_PORT_WARN_NONLOCAL (1u<<1)
#define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2) #define CL_PORT_ALLOW_EXTRA_LISTENADDR (1u<<2)
#define CL_PORT_SERVER_OPTIONS (1u<<3) #define CL_PORT_SERVER_OPTIONS (1u<<3)
#define CL_PORT_FORBID_NONLOCAL (1u<<4) #define CL_PORT_FORBID_NONLOCAL (1u<<4)
#define CL_PORT_TAKES_HOSTNAMES (1u<<5) #define CL_PORT_TAKES_HOSTNAMES (1u<<5)
#define CL_PORT_IS_UNIXSOCKET (1u<<6) #define CL_PORT_IS_UNIXSOCKET (1u<<6)
#define CL_PORT_DFLT_GROUP_WRITABLE (1u<<7)
#ifdef HAVE_SYS_UN_H #ifdef HAVE_SYS_UN_H
@ -5729,7 +5730,7 @@ config_parse_unix_port(const char *addrport, char **path_out)
* If no address is specified, default to <b>defaultaddr</b>. If no * If no address is specified, default to <b>defaultaddr</b>. If no
* FooPort is given, default to defaultport (if 0, there is no default). * FooPort is given, default to defaultport (if 0, there is no default).
* *
* If CL_PORT_NO_OPTIONS is set in <b>flags</b>, do not allow stream * If CL_PORT_NO_STREAM_OPTIONS is set in <b>flags</b>, do not allow stream
* isolation options in the FooPort entries. * isolation options in the FooPort entries.
* *
* If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the * If CL_PORT_WARN_NONLOCAL is set in <b>flags</b>, warn if any of the
@ -5764,10 +5765,12 @@ parse_port_config(smartlist_t *out,
int retval = -1; int retval = -1;
const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER); const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER); const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS; const unsigned allow_no_stream_options = flags & CL_PORT_NO_STREAM_OPTIONS;
const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS; const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL; const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL; const unsigned forbid_nonlocal = flags & CL_PORT_FORBID_NONLOCAL;
const unsigned default_to_group_writable =
flags & CL_PORT_DFLT_GROUP_WRITABLE;
const unsigned allow_spurious_listenaddr = const unsigned allow_spurious_listenaddr =
flags & CL_PORT_ALLOW_EXTRA_LISTENADDR; flags & CL_PORT_ALLOW_EXTRA_LISTENADDR;
const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES; const unsigned takes_hostnames = flags & CL_PORT_TAKES_HOSTNAMES;
@ -5891,7 +5894,7 @@ parse_port_config(smartlist_t *out,
ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0, ipv4_traffic = 1, ipv6_traffic = 0, prefer_ipv6 = 0,
cache_ipv4 = 1, use_cached_ipv4 = 0, cache_ipv4 = 1, use_cached_ipv4 = 0,
cache_ipv6 = 0, use_cached_ipv6 = 0, cache_ipv6 = 0, use_cached_ipv6 = 0,
prefer_ipv6_automap = 1; prefer_ipv6_automap = 1, world_writable = 0, group_writable = 0;
smartlist_split_string(elts, ports->value, NULL, smartlist_split_string(elts, ports->value, NULL,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
@ -5900,11 +5903,6 @@ parse_port_config(smartlist_t *out,
goto err; goto err;
} }
if (allow_no_options && smartlist_len(elts) > 1) {
log_warn(LD_CONFIG, "Too many options on %sPort line", portname);
goto err;
}
/* Now parse the addr/port value */ /* Now parse the addr/port value */
addrport = smartlist_get(elts, 0); addrport = smartlist_get(elts, 0);
@ -5962,6 +5960,9 @@ parse_port_config(smartlist_t *out,
} }
} }
if (unix_socket_path && default_to_group_writable)
group_writable = 1;
/* Now parse the rest of the options, if any. */ /* Now parse the rest of the options, if any. */
if (use_server_options) { if (use_server_options) {
/* This is a server port; parse advertising options */ /* This is a server port; parse advertising options */
@ -6018,10 +6019,11 @@ parse_port_config(smartlist_t *out,
const char *elt_orig = elt; const char *elt_orig = elt;
if (elt_sl_idx == 0) if (elt_sl_idx == 0)
continue; /* Skip addr:port */ continue; /* Skip addr:port */
if (!strcasecmpstart(elt, "SessionGroup=")) { if (!strcasecmpstart(elt, "SessionGroup=")) {
int group = (int)tor_parse_long(elt+strlen("SessionGroup="), int group = (int)tor_parse_long(elt+strlen("SessionGroup="),
10, 0, INT_MAX, &ok, NULL); 10, 0, INT_MAX, &ok, NULL);
if (!ok) { if (!ok || !allow_no_stream_options) {
log_warn(LD_CONFIG, "Invalid %sPort option '%s'", log_warn(LD_CONFIG, "Invalid %sPort option '%s'",
portname, escaped(elt)); portname, escaped(elt));
goto err; goto err;
@ -6040,6 +6042,18 @@ parse_port_config(smartlist_t *out,
elt += 2; elt += 2;
} }
if (!strcasecmp(elt, "GroupWritable")) {
group_writable = !no;
} else if (!strcasecmp(elt, "WorldWritable")) {
world_writable = !no;
}
if (allow_no_stream_options) {
log_warn(LD_CONFIG, "Unrecognized %sPort option '%s'",
portname, escaped(elt));
continue;
}
if (takes_hostnames) { if (takes_hostnames) {
if (!strcasecmp(elt, "IPv4Traffic")) { if (!strcasecmp(elt, "IPv4Traffic")) {
ipv4_traffic = ! no; ipv4_traffic = ! no;
@ -6115,6 +6129,12 @@ parse_port_config(smartlist_t *out,
goto err; goto err;
} }
if ( (world_writable || group_writable) && ! unix_socket_path) {
log_warn(LD_CONFIG, "You have a %sPort entry with GroupWritable "
"or WorldWritable set, but it is not a unix socket.", portname);
goto err;
}
if (out && port) { if (out && port) {
size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0; size_t namelen = unix_socket_path ? strlen(unix_socket_path) : 0;
port_cfg_t *cfg = port_cfg_new(namelen); port_cfg_t *cfg = port_cfg_new(namelen);
@ -6128,6 +6148,8 @@ parse_port_config(smartlist_t *out,
cfg->port = port; cfg->port = port;
} }
cfg->type = listener_type; cfg->type = listener_type;
cfg->is_world_writable = world_writable;
cfg->is_group_writable = group_writable;
cfg->entry_cfg.isolation_flags = isolation; cfg->entry_cfg.isolation_flags = isolation;
cfg->entry_cfg.session_group = sessiongroup; cfg->entry_cfg.session_group = sessiongroup;
cfg->server_cfg.no_advertise = no_advertise; cfg->server_cfg.no_advertise = no_advertise;
@ -6214,12 +6236,14 @@ parse_ports(or_options_t *options, int validate_only,
*n_ports_out = 0; *n_ports_out = 0;
const unsigned gw_flag = options->SocksSocketsGroupWritable ?
CL_PORT_DFLT_GROUP_WRITABLE : 0;
if (parse_port_config(ports, if (parse_port_config(ports,
options->SocksPort_lines, options->SocksListenAddress, options->SocksPort_lines, options->SocksListenAddress,
"Socks", CONN_TYPE_AP_LISTENER, "Socks", CONN_TYPE_AP_LISTENER,
"127.0.0.1", 9050, "127.0.0.1", 9050,
CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR| CL_PORT_WARN_NONLOCAL|CL_PORT_ALLOW_EXTRA_LISTENADDR|
CL_PORT_TAKES_HOSTNAMES) < 0) { CL_PORT_TAKES_HOSTNAMES|gw_flag) < 0) {
*msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration"); *msg = tor_strdup("Invalid SocksPort/SocksListenAddress configuration");
goto err; goto err;
} }
@ -6248,12 +6272,15 @@ parse_ports(or_options_t *options, int validate_only,
goto err; goto err;
} }
{ {
unsigned control_port_flags = CL_PORT_NO_OPTIONS | CL_PORT_WARN_NONLOCAL; unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS |
CL_PORT_WARN_NONLOCAL;
const int any_passwords = (options->HashedControlPassword || const int any_passwords = (options->HashedControlPassword ||
options->HashedControlSessionPassword || options->HashedControlSessionPassword ||
options->CookieAuthentication); options->CookieAuthentication);
if (! any_passwords) if (! any_passwords)
control_port_flags |= CL_PORT_FORBID_NONLOCAL; control_port_flags |= CL_PORT_FORBID_NONLOCAL;
if (options->ControlSocketsGroupWritable)
control_port_flags |= CL_PORT_DFLT_GROUP_WRITABLE;
if (parse_port_config(ports, if (parse_port_config(ports,
options->ControlPort_lines, options->ControlPort_lines,

View File

@ -972,7 +972,7 @@ unix_socket_purpose_to_string(int purpose)
* <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */ * <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */
static int static int
check_location_for_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 purpose, const port_cfg_t *port)
{ {
int r = -1; int r = -1;
char *p = NULL; char *p = NULL;
@ -987,10 +987,13 @@ check_location_for_unix_socket(const or_options_t *options, const char *path,
goto done; goto done;
} }
if ((purpose == UNIX_SOCKET_PURPOSE_CONTROL_SOCKET && if (port->is_world_writable) {
options->ControlSocketsGroupWritable) || /* World-writable sockets can go anywhere. */
(purpose == UNIX_SOCKET_PURPOSE_SOCKS_SOCKET && r = 0;
options->SocksSocketsGroupWritable)) { goto done;
}
if (port->is_group_writable) {
flags |= CPD_GROUP_OK; flags |= CPD_GROUP_OK;
} }
@ -1004,7 +1007,7 @@ check_location_for_unix_socket(const or_options_t *options, const char *path,
"who can list a socket can connect to it, so Tor is being " "who can list a socket can connect to it, so Tor is being "
"careful.)", "careful.)",
unix_socket_purpose_to_string(purpose), escpath, escdir, unix_socket_purpose_to_string(purpose), escpath, escdir,
options->ControlSocketsGroupWritable ? " and group" : ""); port->is_group_writable ? " and group" : "");
tor_free(escpath); tor_free(escpath);
tor_free(escdir); tor_free(escdir);
goto done; goto done;
@ -1198,7 +1201,7 @@ connection_listener_new(const struct sockaddr *listensockaddr,
if (check_location_for_unix_socket(options, address, if (check_location_for_unix_socket(options, address,
(type == CONN_TYPE_CONTROL_LISTENER) ? (type == CONN_TYPE_CONTROL_LISTENER) ?
UNIX_SOCKET_PURPOSE_CONTROL_SOCKET : UNIX_SOCKET_PURPOSE_CONTROL_SOCKET :
UNIX_SOCKET_PURPOSE_SOCKS_SOCKET) < 0) { UNIX_SOCKET_PURPOSE_SOCKS_SOCKET, port_cfg) < 0) {
goto err; goto err;
} }
@ -1241,24 +1244,23 @@ connection_listener_new(const struct sockaddr *listensockaddr,
} }
#endif #endif
if ((type == CONN_TYPE_CONTROL_LISTENER && {
options->ControlSocketsGroupWritable) || unsigned mode;
(type == CONN_TYPE_AP_LISTENER && const char *status;
options->SocksSocketsGroupWritable)) { if (port_cfg->is_world_writable) {
/* We need to use chmod; fchmod doesn't work on sockets on all mode = 0666;
* platforms. */ status = "world-writable";
if (chmod(address, 0660) < 0) { } else if (port_cfg->is_group_writable) {
log_warn(LD_FS,"Unable to make %s group-writable.", address); mode = 0660;
goto err; status = "group-writable";
} else {
mode = 0600;
status = "private";
} }
} 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 /* We need to use chmod; fchmod doesn't work on sockets on all
* platforms. */ * platforms. */
if (chmod(address, 0600) < 0) { if (chmod(address, mode) < 0) {
log_warn(LD_FS,"Unable to make %s group-writable.", address); log_warn(LD_FS,"Unable to make %s %s.", address, status);
goto err; goto err;
} }
} }

View File

@ -3322,6 +3322,9 @@ typedef struct port_cfg_t {
uint8_t type; /**< One of CONN_TYPE_*_LISTENER */ uint8_t type; /**< One of CONN_TYPE_*_LISTENER */
unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */ unsigned is_unix_addr : 1; /**< True iff this is an AF_UNIX address. */
unsigned is_group_writable : 1;
unsigned is_world_writable : 1;
entry_port_cfg_t entry_cfg; entry_port_cfg_t entry_cfg;
server_port_cfg_t server_cfg; server_port_cfg_t server_cfg;