diff options
author | Nick Mathewson <nickm@torproject.org> | 2011-05-16 11:01:23 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2011-05-16 11:01:23 -0400 |
commit | fd105e1048001ec4498116e7db5304bf7a1c90ca (patch) | |
tree | 9271c04d6cc726ef130d44843df8ef73590ead73 | |
parent | da805cea47ee518ab1481e1547c7f8704dbf19f4 (diff) | |
parent | 83fe07d3f26aff703230305c9ff529592af32d4f (diff) | |
download | tor-fd105e1048001ec4498116e7db5304bf7a1c90ca.tar.gz tor-fd105e1048001ec4498116e7db5304bf7a1c90ca.zip |
Merge remote-tracking branch 'origin/maint-0.2.2' into release-0.2.2
-rw-r--r-- | changes/bug1345 | 13 | ||||
-rw-r--r-- | changes/bug2732-simple | 7 | ||||
-rw-r--r-- | changes/bug2792_checkdir | 8 | ||||
-rw-r--r-- | changes/bug2972 | 5 | ||||
-rw-r--r-- | changes/bug3019 | 4 | ||||
-rw-r--r-- | changes/bug3026 | 4 | ||||
-rw-r--r-- | changes/bug3175 | 7 | ||||
-rw-r--r-- | doc/tor.1.txt | 5 | ||||
-rw-r--r-- | src/common/compat.c | 39 | ||||
-rw-r--r-- | src/common/compat.h | 2 | ||||
-rw-r--r-- | src/common/container.c | 21 | ||||
-rw-r--r-- | src/common/container.h | 4 | ||||
-rw-r--r-- | src/common/di_ops.c | 14 | ||||
-rw-r--r-- | src/common/di_ops.h | 1 | ||||
-rw-r--r-- | src/common/util.c | 58 | ||||
-rw-r--r-- | src/common/util.h | 7 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 57 | ||||
-rw-r--r-- | src/or/circuitbuild.h | 9 | ||||
-rw-r--r-- | src/or/circuituse.c | 51 | ||||
-rw-r--r-- | src/or/circuituse.h | 2 | ||||
-rw-r--r-- | src/or/config.c | 40 | ||||
-rw-r--r-- | src/or/connection.c | 49 | ||||
-rw-r--r-- | src/or/connection_edge.c | 51 | ||||
-rw-r--r-- | src/or/connection_edge.h | 1 | ||||
-rw-r--r-- | src/or/directory.c | 5 | ||||
-rw-r--r-- | src/or/dirvote.c | 13 | ||||
-rw-r--r-- | src/or/main.c | 1 | ||||
-rw-r--r-- | src/or/or.h | 4 | ||||
-rw-r--r-- | src/or/routerlist.c | 27 | ||||
-rw-r--r-- | src/test/test_util.c | 30 |
30 files changed, 450 insertions, 89 deletions
diff --git a/changes/bug1345 b/changes/bug1345 new file mode 100644 index 0000000000..0c9375a35d --- /dev/null +++ b/changes/bug1345 @@ -0,0 +1,13 @@ + o Minor bugfixes: + - On SIGHUP, do not clear out all TrackHostExits mappings, client DNS + cache entries, and virtual address mappings: that's what NEWNYM is + for. Bugfix on Tor 0.1.0.1-rc; fixes bug 1345. + - When TrackHostExits is changed from a controller, remove any + mappings for hosts that should no longer have their exits tracked. + Bugfix on Tor 0.1.0.1-rc. + - When VirtualAddrNetwork option is changed from a controller, + remove any mappings for hosts that were automapped to + that network. Bugfix on 0.1.1.19-rc. + - When one of the AutomapHosts* options is changed from a + controller, remove any mappings for hosts that should no longer be + automapped. Bugfix on 0.2.0.1-alpha. diff --git a/changes/bug2732-simple b/changes/bug2732-simple new file mode 100644 index 0000000000..367836152d --- /dev/null +++ b/changes/bug2732-simple @@ -0,0 +1,7 @@ + o Minor bugfixes + - Do not reject hidden service descriptors simply because we don't + think we have not been assigned the HSDir flag. Clients and + hidden services can have a more up-to-date view of the network + consensus, and if they think that the directory authorities + list us a HSDir, we might actually be one. Related to bug 2732; + bugfix on 0.2.0.10-alpha. diff --git a/changes/bug2792_checkdir b/changes/bug2792_checkdir new file mode 100644 index 0000000000..10de1deb2d --- /dev/null +++ b/changes/bug2792_checkdir @@ -0,0 +1,8 @@ + o Minor features: + - Tor now refuses to create a ControlSocket in a directory that is + world-readable (or group-readable if ControlSocketsGroupWritable + is 0). This is necessary because some operating systems do not + check the permissions on an AF_UNIX socket when programs try to + connect to it. Checking permissions on the directory holding + the socket, however, seems to work everywhere. + diff --git a/changes/bug2972 b/changes/bug2972 new file mode 100644 index 0000000000..26afcca421 --- /dev/null +++ b/changes/bug2972 @@ -0,0 +1,5 @@ + o Minor features: + - Allow ControlSockets to be group-writable when the + ControlSocksGroupWritable configuration option is turned on. Patch + by Jérémy Bobbio; implements ticket 2972. + diff --git a/changes/bug3019 b/changes/bug3019 new file mode 100644 index 0000000000..4df709fb3b --- /dev/null +++ b/changes/bug3019 @@ -0,0 +1,4 @@ + o Minor bugfixes: + - Do not reset the bridge descriptor download status every time we + re-parse our configuration or get a configuration change. Fixes + bug 3019; bugfix on Tor 0.2.0.3-alpha. diff --git a/changes/bug3026 b/changes/bug3026 new file mode 100644 index 0000000000..c0c0a3860a --- /dev/null +++ b/changes/bug3026 @@ -0,0 +1,4 @@ + o Minor bugfixes (directory authority) + - Do not upload our own vote or signature set to ourself. It would + tell us nothing new. Also, as of Tor 0.2.2.24-alpha, we started + to warn about receiving duplicate votes. Resolves bug 3026. diff --git a/changes/bug3175 b/changes/bug3175 new file mode 100644 index 0000000000..3360fbce00 --- /dev/null +++ b/changes/bug3175 @@ -0,0 +1,7 @@ + o Minor bugfixes: + - Resolve an untriggerable issue in smartlist_string_num_isin(), + where if the function had ever in the future been used to check + for the presence of a too-large number, it would have given an + incorrect result. (Fortunately, we only used it for 16-bit + values.) Fixes bug 3175; bugfix on Tor 0.1.0.1-rc. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index d95d764c67..1815a8d963 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -167,6 +167,11 @@ Other options can be specified either on the command-line (--option Like ControlPort, but listens on a Unix domain socket, rather than a TCP socket. (Unix and Unix-like systems only.) +**ControlSocketsGroupWritable** **0**|**1**:: + If this option is set to 0, don't allow the filesystem group to read and + write unix sockets (e.g. ControlSocket). If the option is set to 1, make + the control socket readable and writable by the default GID. (Default: 0) + **HashedControlPassword** __hashed_password__:: Don't allow any connections on the control port except when the other process knows the password whose one-way hash is __hashed_password__. You diff --git a/src/common/compat.c b/src/common/compat.c index ea7f9d7efc..fc066da681 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1467,6 +1467,45 @@ get_user_homedir(const char *username) } #endif +/** Modify <b>fname</b> to contain the name of the directory */ +int +get_parent_directory(char *fname) +{ + char *cp; + int at_end = 1; + tor_assert(fname); +#ifdef MS_WINDOWS + /* If we start with, say, c:, then don't consider that the start of the path + */ + if (fname[0] && fname[1] == ':') { + fname += 2; + } +#endif + /* Now we want to remove all path-separators at the end of the string, + * and to remove the end of the string starting with the path separator + * before the last non-path-separator. In perl, this would be + * s#[/]*$##; s#/[^/]*$##; + * on a unixy platform. + */ + cp = fname + strlen(fname); + at_end = 1; + while (--cp > fname) { + int is_sep = (*cp == '/' +#ifdef MS_WINDOWS + || *cp == '\\' +#endif + ); + if (is_sep) { + *cp = '\0'; + if (! at_end) + return 0; + } else { + at_end = 0; + } + } + return -1; +} + /** Set *addr to the IP address (in dotted-quad notation) stored in c. * Return 1 on success, 0 if c is badly formatted. (Like inet_aton(c,addr), * but works on Windows and Solaris.) diff --git a/src/common/compat.h b/src/common/compat.h index af795ffba9..eff51ab30c 100644 --- a/src/common/compat.h +++ b/src/common/compat.h @@ -552,6 +552,8 @@ int switch_id(const char *user); char *get_user_homedir(const char *username); #endif +int get_parent_directory(char *fname); + int spawn_func(void (*func)(void *), void *data); void spawn_exit(void) ATTR_NORETURN; diff --git a/src/common/container.c b/src/common/container.c index 09d4bb131f..da44b7fe68 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -210,11 +210,30 @@ smartlist_string_isin_case(const smartlist_t *sl, const char *element) int smartlist_string_num_isin(const smartlist_t *sl, int num) { - char buf[16]; + char buf[32]; /* long enough for 64-bit int, and then some. */ tor_snprintf(buf,sizeof(buf),"%d", num); return smartlist_string_isin(sl, buf); } +/** Return true iff the two lists contain the same strings in the same + * order, or if they are both NULL. */ +int +smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) +{ + if (sl1 == NULL) + return sl2 == NULL; + if (sl2 == NULL) + return 0; + if (smartlist_len(sl1) != smartlist_len(sl2)) + return 0; + SMARTLIST_FOREACH(sl1, const char *, cp1, { + const char *cp2 = smartlist_get(sl2, cp1_sl_idx); + if (strcmp(cp1, cp2)) + return 0; + }); + return 1; +} + /** Return true iff <b>sl</b> has some element E such that * tor_memeq(E,<b>element</b>,DIGEST_LEN) */ diff --git a/src/common/container.h b/src/common/container.h index b39d4ca07e..4a6eba789d 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -42,6 +42,8 @@ int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE; int smartlist_string_isin_case(const smartlist_t *sl, const char *element) ATTR_PURE; int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; +int smartlist_strings_eq(const smartlist_t *sl1, const smartlist_t *sl2) + ATTR_PURE; int smartlist_digest_isin(const smartlist_t *sl, const char *element) ATTR_PURE; int smartlist_overlap(const smartlist_t *sl1, const smartlist_t *sl2) @@ -259,7 +261,7 @@ char *smartlist_join_strings2(smartlist_t *sl, const char *join, * Example use: * SMARTLIST_FOREACH_JOIN(routerstatus_list, routerstatus_t *, rs, * routerinfo_list, routerinfo_t *, ri, - * tor_memcmp(rs->identity_digest, ri->identity_digest, 20), + * tor_memcmp(rs->identity_digest, ri->identity_digest, 20), * log_info(LD_GENERAL,"No match for %s", ri->nickname)) { * log_info(LD_GENERAL, "%s matches routerstatus %p", ri->nickname, rs); * } SMARTLIST_FOREACH_JOIN_END(rs, ri); diff --git a/src/common/di_ops.c b/src/common/di_ops.c index c1e292fe2f..b22a58d1b1 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -3,18 +3,18 @@ /** * \file di_ops.c - * \brief Functions for data-independent operations + * \brief Functions for data-independent operations. **/ #include "orconfig.h" #include "di_ops.h" /** - * Timing-safe version of memcmp. As memcmp, compare the <b>sz</b> bytes - * at <b>a</b> with the <b>sz</b> bytes at <b>, and returns less than 0 if the - * bytes at <b>a</b> lexically precede those at <b>b</b>, 0 if the byte ranges - * are equal, and greater than zero if the bytes at <b>a</b> lexically follow - * those at <b>. + * Timing-safe version of memcmp. As memcmp, compare the <b>sz</b> bytes at + * <b>a</b> with the <b>sz</b> bytes at <b>b</b>, and return less than 0 if + * the bytes at <b>a</b> lexically precede those at <b>b</b>, 0 if the byte + * ranges are equal, and greater than zero if the bytes at <b>a</b> lexically + * follow those at <b>b</b>. * * This implementation differs from memcmp in that its timing behavior is not * data-dependent: it should return in the same amount of time regardless of @@ -85,7 +85,7 @@ tor_memcmp(const void *a, const void *b, size_t len) /** * Timing-safe memory comparison. Return true if the <b>sz</b> bytes at - * <b>a</b> are the same as the <b>sz</b> bytes at <b>, and 0 otherwise. + * <b>a</b> are the same as the <b>sz</b> bytes at <b>b</b>, and 0 otherwise. * * This implementation differs from !memcmp(a,b,sz) in that its timing * behavior is not data-dependent: it should return in the same amount of time diff --git a/src/common/di_ops.h b/src/common/di_ops.h index 4a212b0ca2..fa7d86806a 100644 --- a/src/common/di_ops.h +++ b/src/common/di_ops.h @@ -28,3 +28,4 @@ int tor_memeq(const void *a, const void *b, size_t sz); #define fast_memneq(a,b,c) (0!=memcmp((a),(b),(c))) #endif + diff --git a/src/common/util.c b/src/common/util.c index 86f4141674..1bb116b212 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -30,6 +30,7 @@ #else #include <dirent.h> #include <pwd.h> +#include <grp.h> #endif /* math.h needs this on Linux */ @@ -1664,17 +1665,25 @@ file_status(const char *fname) return FN_ERROR; } -/** Check whether dirname exists and is private. If yes return 0. If - * it does not exist, and check==CPD_CREATE is set, try to create it +/** Check whether <b>dirname</b> exists and is private. If yes return 0. If + * it does not exist, and <b>check</b>&CPD_CREATE is set, try to create it * and return 0 on success. If it does not exist, and - * check==CPD_CHECK, and we think we can create it, return 0. Else - * return -1. */ + * <b>check</b>&CPD_CHECK, and we think we can create it, return 0. Else + * return -1. If CPD_GROUP_OK is set, then it's okay if the directory + * is group-readable, but in all cases we create the directory mode 0700. + * If CPD_CHECK_MODE_ONLY is set, then we don't alter the directory permissions + * if they are too permissive: we just return -1. + */ int check_private_dir(const char *dirname, cpd_check_t check) { int r; struct stat st; char *f; +#ifndef MS_WINDOWS + int mask; +#endif + tor_assert(dirname); f = tor_strdup(dirname); clean_name_for_stat(f); @@ -1686,10 +1695,7 @@ check_private_dir(const char *dirname, cpd_check_t check) strerror(errno)); return -1; } - if (check == CPD_NONE) { - log_warn(LD_FS, "Directory %s does not exist.", dirname); - return -1; - } else if (check == CPD_CREATE) { + if (check & CPD_CREATE) { log_info(LD_GENERAL, "Creating directory %s", dirname); #if defined (MS_WINDOWS) && !defined (WINCE) r = mkdir(dirname); @@ -1701,6 +1707,9 @@ check_private_dir(const char *dirname, cpd_check_t check) strerror(errno)); return -1; } + } else if (!(check & CPD_CHECK)) { + log_warn(LD_FS, "Directory %s does not exist.", dirname); + return -1; } /* XXXX In the case where check==CPD_CHECK, we should look at the * parent directory a little harder. */ @@ -1728,9 +1737,38 @@ check_private_dir(const char *dirname, cpd_check_t check) tor_free(process_ownername); return -1; } - if (st.st_mode & 0077) { + if ((check & CPD_GROUP_OK) && st.st_gid != getgid()) { + struct group *gr; + char *process_groupname = NULL; + gr = getgrgid(getgid()); + process_groupname = gr ? tor_strdup(gr->gr_name) : tor_strdup("<unknown>"); + gr = getgrgid(st.st_gid); + + log_warn(LD_FS, "%s is not owned by this group (%s, %d) but by group " + "%s (%d). Are you running Tor as the wrong user?", + dirname, process_groupname, (int)getgid(), + gr ? gr->gr_name : "<unknown>", (int)st.st_gid); + + tor_free(process_groupname); + return -1; + } + if (check & CPD_GROUP_OK) { + mask = 0027; + } else { + mask = 0077; + } + if (st.st_mode & mask) { + unsigned new_mode; + if (check & CPD_CHECK_MODE_ONLY) { + log_warn(LD_FS, "Permissions on directory %s are too permissive.", + dirname); + return -1; + } log_warn(LD_FS, "Fixing permissions on directory %s", dirname); - if (chmod(dirname, 0700)) { + new_mode = st.st_mode; + new_mode |= 0700; /* Owner should have rwx */ + new_mode &= ~mask; /* Clear the other bits that we didn't want set...*/ + if (chmod(dirname, new_mode)) { log_warn(LD_FS, "Could not chmod directory %s: %s", dirname, strerror(errno)); return -1; diff --git a/src/common/util.h b/src/common/util.h index 961b5875ad..f32709accd 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -286,7 +286,12 @@ file_status_t file_status(const char *filename); /** Possible behaviors for check_private_dir() on encountering a nonexistent * directory; see that function's documentation for details. */ -typedef enum { CPD_NONE, CPD_CREATE, CPD_CHECK } cpd_check_t; +typedef unsigned int cpd_check_t; +#define CPD_NONE 0 +#define CPD_CREATE 1 +#define CPD_CHECK 2 +#define CPD_GROUP_OK 4 +#define CPD_CHECK_MODE_ONLY 8 int check_private_dir(const char *dirname, cpd_check_t check); #define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC) #define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index a9986d309c..860cd27567 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -4471,6 +4471,9 @@ typedef struct { tor_addr_t addr; /** TLS port for the bridge. */ uint16_t port; + /** Boolean: We are re-parsing our bridge list, and we are going to remove + * this one if we don't find it in the list of configured bridges. */ + unsigned marked_for_removal : 1; /** Expected identity digest, or all zero bytes if we don't know what the * digest should be. */ char identity[DIGEST_LEN]; @@ -4479,11 +4482,39 @@ typedef struct { } bridge_info_t; /** A list of configured bridges. Whenever we actually get a descriptor - * for one, we add it as an entry guard. */ + * for one, we add it as an entry guard. Note that the order of bridges + * in this list does not necessarily correspond to the order of bridges + * in the torrc. */ static smartlist_t *bridge_list = NULL; -/** Initialize the bridge list to empty, creating it if needed. */ +/** Mark every entry of the bridge list to be removed on our next call to + * sweep_bridge_list unless it has first been un-marked. */ +void +mark_bridge_list(void) +{ + if (!bridge_list) + bridge_list = smartlist_create(); + SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, + b->marked_for_removal = 1); +} + +/** Remove every entry of the bridge list that was marked with + * mark_bridge_list if it has not subsequently been un-marked. */ void +sweep_bridge_list(void) +{ + if (!bridge_list) + bridge_list = smartlist_create(); + SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) { + if (b->marked_for_removal) { + SMARTLIST_DEL_CURRENT(bridge_list, b); + tor_free(b); + } + } SMARTLIST_FOREACH_END(b); +} + +/** Initialize the bridge list to empty, creating it if needed. */ +static void clear_bridge_list(void) { if (!bridge_list) @@ -4496,7 +4527,8 @@ clear_bridge_list(void) * (either by comparing keys if possible, else by comparing addr/port). * Else return NULL. */ static bridge_info_t * -get_configured_bridge_by_addr_port_digest(tor_addr_t *addr, uint16_t port, +get_configured_bridge_by_addr_port_digest(const tor_addr_t *addr, + uint16_t port, const char *digest) { if (!bridge_list) @@ -4537,7 +4569,8 @@ routerinfo_is_a_configured_bridge(routerinfo_t *ri) * If it was a bridge, and we still don't know its digest, record it. */ void -learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest) +learned_router_identity(const tor_addr_t *addr, uint16_t port, + const char *digest) { bridge_info_t *bridge = get_configured_bridge_by_addr_port_digest(addr, port, digest); @@ -4549,11 +4582,20 @@ learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest) } /** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b> - * is set, it tells us the identity key too. */ + * is set, it tells us the identity key too. If we already had the + * bridge in our list, unmark it, and don't actually add anything new. */ void -bridge_add_from_config(const tor_addr_t *addr, uint16_t port, char *digest) +bridge_add_from_config(const tor_addr_t *addr, uint16_t port, + const char *digest) { - bridge_info_t *b = tor_malloc_zero(sizeof(bridge_info_t)); + bridge_info_t *b; + + if ((b = get_configured_bridge_by_addr_port_digest(addr, port, digest))) { + b->marked_for_removal = 0; + return; + } + + b = tor_malloc_zero(sizeof(bridge_info_t)); tor_addr_copy(&b->addr, addr); b->port = port; if (digest) @@ -4561,6 +4603,7 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port, char *digest) b->fetch_status.schedule = DL_SCHED_BRIDGE; if (!bridge_list) bridge_list = smartlist_create(); + smartlist_add(bridge_list, b); } diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h index af24931878..0e673e1c05 100644 --- a/src/or/circuitbuild.h +++ b/src/or/circuitbuild.h @@ -62,12 +62,13 @@ int getinfo_helper_entry_guards(control_connection_t *conn, const char *question, char **answer, const char **errmsg); -void clear_bridge_list(void); +void mark_bridge_list(void); +void sweep_bridge_list(void); int routerinfo_is_a_configured_bridge(routerinfo_t *ri); -void -learned_router_identity(tor_addr_t *addr, uint16_t port, const char *digest); +void learned_router_identity(const tor_addr_t *addr, uint16_t port, + const char *digest); void bridge_add_from_config(const tor_addr_t *addr, uint16_t port, - char *digest); + const char *digest); void retry_bridge_descriptor_fetch_directly(const char *digest); void fetch_bridge_descriptors(or_options_t *options, time_t now); void learned_bridge_descriptor(routerinfo_t *ri, int from_cache); diff --git a/src/or/circuituse.c b/src/or/circuituse.c index e68fb4fa82..7289aa5c11 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1485,15 +1485,35 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ, } } -/** If an exit wasn't specifically chosen, save the history for future - * use. */ +/** Return true iff <b>address</b> is matched by one of the entries in + * TrackHostExits. */ +int +hostname_in_track_host_exits(or_options_t *options, const char *address) +{ + if (!options->TrackHostExits) + return 0; + SMARTLIST_FOREACH_BEGIN(options->TrackHostExits, const char *, cp) { + if (cp[0] == '.') { /* match end */ + if (cp[1] == '\0' || + !strcasecmpend(address, cp) || + !strcasecmp(address, &cp[1])) + return 1; + } else if (strcasecmp(cp, address) == 0) { + return 1; + } + } SMARTLIST_FOREACH_END(cp); + return 0; +} + +/** If an exit wasn't explicitly specified for <b>conn</b>, consider saving + * the exit that we *did* choose for use by future connections to + * <b>conn</b>'s destination. + */ static void consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ) { - int found_needle = 0; or_options_t *options = get_options(); - size_t len; - char *new_address; + char *new_address = NULL; char fp[HEX_DIGEST_LEN+1]; /* Search the addressmap for this conn's destination. */ @@ -1503,18 +1523,8 @@ consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ) options->TrackHostExitsExpire)) return; /* nothing to track, or already mapped */ - SMARTLIST_FOREACH(options->TrackHostExits, const char *, cp, { - if (cp[0] == '.') { /* match end */ - if (cp[1] == '\0' || - !strcasecmpend(conn->socks_request->address, cp) || - !strcasecmp(conn->socks_request->address, &cp[1])) - found_needle = 1; - } else if (strcasecmp(cp, conn->socks_request->address) == 0) { - found_needle = 1; - } - }); - - if (!found_needle || !circ->build_state->chosen_exit) + if (!hostname_in_track_host_exits(options, conn->socks_request->address) || + !circ->build_state->chosen_exit) return; /* write down the fingerprint of the chosen exit, not the nickname, @@ -1523,12 +1533,7 @@ consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ) circ->build_state->chosen_exit->identity_digest, DIGEST_LEN); /* Add this exit/hostname pair to the addressmap. */ - len = strlen(conn->socks_request->address) + 1 /* '.' */ + - strlen(fp) + 1 /* '.' */ + - strlen("exit") + 1 /* '\0' */; - new_address = tor_malloc(len); - - tor_snprintf(new_address, len, "%s.%s.exit", + tor_asprintf(&new_address, "%s.%s.exit", conn->socks_request->address, fp); addressmap_register(conn->socks_request->address, new_address, diff --git a/src/or/circuituse.h b/src/or/circuituse.h index 9f393ab378..bfeaea20dc 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -51,5 +51,7 @@ int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn, crypt_path_t *cpath); int connection_ap_handshake_attach_circuit(edge_connection_t *conn); +int hostname_in_track_host_exits(or_options_t *options, const char *address); + #endif diff --git a/src/or/config.c b/src/or/config.c index a7ff28f462..614fc48c3e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -209,6 +209,7 @@ static config_var_t _option_vars[] = { V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), + V(ControlSocketsGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), @@ -952,9 +953,15 @@ options_act_reversible(or_options_t *old_options, char **msg) } #ifndef HAVE_SYS_UN_H - if (options->ControlSocket) { - *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported" - " on this OS/with this build."); + if (options->ControlSocket || options->ControlSocketsGroupWritable) { + *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported " + "on this OS/with this build."); + goto rollback; + } +#else + if (options->ControlSocketsGroupWritable && !options->ControlSocket) { + *msg = tor_strdup("Setting ControlSocketGroupWritable without setting" + "a ControlSocket makes no sense."); goto rollback; } #endif @@ -1172,7 +1179,7 @@ options_act(or_options_t *old_options) return -1; if (options->Bridges) { - clear_bridge_list(); + mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { if (parse_bridge_line(cl->value, 0)<0) { log_warn(LD_BUG, @@ -1180,6 +1187,7 @@ options_act(or_options_t *old_options) return -1; } } + sweep_bridge_list(); } if (running_tor && rend_config_services(options, 0)<0) { @@ -1264,6 +1272,8 @@ options_act(or_options_t *old_options) /* Check for transitions that need action. */ if (old_options) { + int revise_trackexithosts = 0; + int revise_automap_entries = 0; if ((options->UseEntryGuards && !old_options->UseEntryGuards) || !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) || !routerset_equal(old_options->ExcludeExitNodes, @@ -1276,9 +1286,31 @@ options_act(or_options_t *old_options) "excluded node lists. Abandoning previous circuits."); circuit_mark_all_unused_circs(); circuit_expire_all_dirty_circs(); + revise_trackexithosts = 1; + } + + if (!smartlist_strings_eq(old_options->TrackHostExits, + options->TrackHostExits)) + revise_trackexithosts = 1; + + if (revise_trackexithosts) addressmap_clear_excluded_trackexithosts(options); + + if (!options->AutomapHostsOnResolve) { + if (old_options->AutomapHostsOnResolve) + revise_automap_entries = 1; + } else { + if (!smartlist_strings_eq(old_options->AutomapHostsSuffixes, + options->AutomapHostsSuffixes)) + revise_automap_entries = 1; + else if (!opt_streq(old_options->VirtualAddrNetwork, + options->VirtualAddrNetwork)) + revise_automap_entries = 1; } + if (revise_automap_entries) + addressmap_clear_invalid_automaps(options); + /* How long should we delay counting bridge stats after becoming a bridge? * We use this so we don't count people who used our bridge thinking it is * a relay. If you change this, don't forget to change the log message diff --git a/src/or/connection.c b/src/or/connection.c index 01b533d9b5..b7d6fe408d 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -853,6 +853,43 @@ warn_too_many_conns(void) } } +#ifdef HAVE_SYS_UN_H +/** Check whether we should be willing to open an AF_UNIX socket in + * <b>path</b>. Return 0 if we should go ahead and -1 if we shouldn't. */ +static int +check_location_for_unix_socket(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) + goto done; + + if (options->ControlSocketsGroupWritable) + flags |= CPD_GROUP_OK; + + if (check_private_dir(p, flags) < 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 conect 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; +} +#endif + /** Bind a new non-blocking socket listening to the socket described * by <b>listensockaddr</b>. * @@ -947,6 +984,9 @@ connection_create_listener(const struct sockaddr *listensockaddr, * and listeners at the same time */ tor_assert(type == CONN_TYPE_CONTROL_LISTENER); + if (check_location_for_unix_socket(get_options(), address) < 0) + goto err; + log_notice(LD_NET, "Opening %s on %s", conn_type_to_string(type), address); @@ -966,6 +1006,15 @@ connection_create_listener(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } + if (get_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); + tor_close_socket(s); + goto err; + } + } if (listen(s,SOMAXCONN) < 0) { log_warn(LD_NET, "Could not listen on %s: %s", address, diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 037920b688..7828f16386 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -810,7 +810,8 @@ clear_trackexithost_mappings(const char *exitname) } /** Remove all TRACKEXIT mappings from the addressmap for which the target - * host is unknown or no longer allowed. */ + * host is unknown or no longer allowed, or for which the source address + * is no longer in trackexithosts. */ void addressmap_clear_excluded_trackexithosts(or_options_t *options) { @@ -851,7 +852,8 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options) tor_free(nodename); if (!ri || (allow_nodes && !routerset_contains_router(allow_nodes, ri)) || - routerset_contains_router(exclude_nodes, ri)) { + routerset_contains_router(exclude_nodes, ri) || + !hostname_in_track_host_exits(options, address)) { /* We don't know this one, or we want to be rid of it. */ addressmap_ent_remove(address, ent); MAP_DEL_CURRENT(address); @@ -859,6 +861,49 @@ addressmap_clear_excluded_trackexithosts(or_options_t *options) } STRMAP_FOREACH_END; } +/** Remove all AUTOMAP mappings from the addressmap for which the + * source address no longer matches AutomapHostsSuffixes, which is + * no longer allowed by AutomapHostsOnResolve, or for which the + * target address is no longer in the virtual network. */ +void +addressmap_clear_invalid_automaps(or_options_t *options) +{ + int clear_all = !options->AutomapHostsOnResolve; + const smartlist_t *suffixes = options->AutomapHostsSuffixes; + + if (!addressmap) + return; + + if (!suffixes) + clear_all = 1; /* This should be impossible, but let's be sure. */ + + STRMAP_FOREACH_MODIFY(addressmap, src_address, addressmap_entry_t *, ent) { + int remove = clear_all; + if (ent->source != ADDRMAPSRC_AUTOMAP) + continue; /* not an automap mapping. */ + + if (!remove) { + int suffix_found = 0; + SMARTLIST_FOREACH(suffixes, const char *, suffix, { + if (!strcasecmpend(src_address, suffix)) { + suffix_found = 1; + break; + } + }); + if (!suffix_found) + remove = 1; + } + + if (!remove && ! address_is_in_virtual_range(ent->new_address)) + remove = 1; + + if (remove) { + addressmap_ent_remove(src_address, ent); + MAP_DEL_CURRENT(src_address); + } + } STRMAP_FOREACH_END; +} + /** Remove all entries from the addressmap that were set via the * configuration file or the command line. */ void @@ -1370,7 +1415,7 @@ addressmap_register_virtual_address(int type, char *new_address) log_info(LD_APP, "Registering map from %s to %s", *addrp, new_address); if (vent_needs_to_be_added) strmap_set(virtaddress_reversemap, new_address, vent); - addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_CONTROLLER); + addressmap_register(*addrp, new_address, 2, ADDRMAPSRC_AUTOMAP); #if 0 { diff --git a/src/or/connection_edge.h b/src/or/connection_edge.h index 70d0dd2713..8ba2fafd08 100644 --- a/src/or/connection_edge.h +++ b/src/or/connection_edge.h @@ -62,6 +62,7 @@ int address_is_invalid_destination(const char *address, int client); void addressmap_init(void); void addressmap_clear_excluded_trackexithosts(or_options_t *options); +void addressmap_clear_invalid_automaps(or_options_t *options); void addressmap_clean(time_t now); void addressmap_clear_configured(void); void addressmap_clear_transient(void); diff --git a/src/or/directory.c b/src/or/directory.c index 4c26671c70..347ed42cb8 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -279,6 +279,8 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, int post_via_tor; smartlist_t *dirservers = router_get_trusted_dir_servers(); int found = 0; + const int exclude_self = (dir_purpose == DIR_PURPOSE_UPLOAD_VOTE || + dir_purpose == DIR_PURPOSE_UPLOAD_SIGNATURES); tor_assert(dirservers); /* This tries dirservers which we believe to be down, but ultimately, that's * harmless, and we may as well err on the side of getting things uploaded. @@ -291,6 +293,9 @@ directory_post_to_dirservers(uint8_t dir_purpose, uint8_t router_purpose, if ((type & ds->type) == 0) continue; + if (exclude_self && router_digest_is_me(ds->digest)) + continue; + if (options->ExcludeNodes && options->StrictNodes && routerset_contains_routerstatus(options->ExcludeNodes, rs)) { log_warn(LD_DIR, "Wanted to contact authority '%s' for %s, but " diff --git a/src/or/dirvote.c b/src/or/dirvote.c index dd156bd9d9..96e3df5cec 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -397,8 +397,9 @@ compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b) if ((r = fast_memcmp(a->status.identity_digest, b->status.identity_digest, DIGEST_LEN))) return r; - if ((r = fast_memcmp(a->status.descriptor_digest, b->status.descriptor_digest, - DIGEST_LEN))) + if ((r = fast_memcmp(a->status.descriptor_digest, + b->status.descriptor_digest, + DIGEST_LEN))) return r; if ((r = (int)(b->status.published_on - a->status.published_on))) return r; @@ -1705,7 +1706,8 @@ networkstatus_compute_consensus(smartlist_t *votes, if (index[v_sl_idx] < size[v_sl_idx]) { rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]); if (!lowest_id || - fast_memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN) < 0) + fast_memcmp(rs->status.identity_digest, + lowest_id, DIGEST_LEN) < 0) lowest_id = rs->status.identity_digest; } }); @@ -1769,7 +1771,7 @@ networkstatus_compute_consensus(smartlist_t *votes, rs = compute_routerstatus_consensus(matching_descs, consensus_method, microdesc_digest); /* Copy bits of that into rs_out. */ - tor_assert(fast_memeq(lowest_id, rs->status.identity_digest, DIGEST_LEN)); + tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN)); memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, DIGEST_LEN); @@ -2211,7 +2213,8 @@ networkstatus_add_detached_signatures(networkstatus_t *target, } for (alg = DIGEST_SHA1; alg < N_DIGEST_ALGORITHMS; ++alg) { if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) { - if (fast_memeq(target->digests.d[alg], digests->d[alg], DIGEST256_LEN)) { + if (fast_memeq(target->digests.d[alg], digests->d[alg], + DIGEST256_LEN)) { ++n_matches; } else { *msg_out = "Mismatched digest."; diff --git a/src/or/main.c b/src/or/main.c index 15682d5400..d700f0e7a8 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -1417,7 +1417,6 @@ do_hup(void) router_reset_warnings(); routerlist_reset_warnings(); - addressmap_clear_transient(); /* first, reload config variables, in case they've changed */ if (options->ReloadTorrcOnSIGHUP) { /* no need to provide argc/v, they've been cached in init_from_config */ diff --git a/src/or/or.h b/src/or/or.h index a73d98ab74..b9d8319ba5 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2445,6 +2445,7 @@ typedef struct { int ControlPort; /**< Port to listen on for control connections. */ config_line_t *ControlSocket; /**< List of Unix Domain Sockets to listen on * for control connections. */ + int ControlSocketsGroupWritable; /**< Boolean: Are control sockets g+rw? */ int DirPort; /**< Port to listen on for directory connections. */ int DNSPort; /**< Port to listen on for DNS requests. */ int AssumeReachable; /**< Whether to publish our descriptor regardless. */ @@ -3150,6 +3151,9 @@ typedef enum setopt_err_t { typedef enum { /** We're remapping this address because the controller told us to. */ ADDRMAPSRC_CONTROLLER, + /** We're remapping this address because of an AutomapHostsOnResolve + * configuration. */ + ADDRMAPSRC_AUTOMAP, /** We're remapping this address because our configuration (via torrc, the * command line, or a SETCONF command) told us to. */ ADDRMAPSRC_TORRC, diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 6c649ab526..a3d9b78ee7 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2332,8 +2332,8 @@ router_get_by_nickname(const char *nickname, int warn_if_unnamed) if (n_matches <= 1 || router->is_running) best_match = router; } else if (maybedigest && - tor_memeq(digest, router->cache_info.identity_digest, DIGEST_LEN) - ) { + tor_memeq(digest, router->cache_info.identity_digest, + DIGEST_LEN)) { if (router_hex_digest_matches(router, nickname)) return router; /* If we reach this point, we have a ID=name syntax that matches the @@ -5063,8 +5063,9 @@ routerinfo_incompatible_with_extrainfo(routerinfo_t *ri, extrainfo_t *ei, /* The identity must match exactly to have been generated at the same time * by the same router. */ - if (tor_memneq(ri->cache_info.identity_digest, ei->cache_info.identity_digest, - DIGEST_LEN)) { + if (tor_memneq(ri->cache_info.identity_digest, + ei->cache_info.identity_digest, + DIGEST_LEN)) { if (msg) *msg = "Extrainfo nickname or identity did not match routerinfo"; goto err; /* different servers */ } @@ -5758,8 +5759,6 @@ int hid_serv_acting_as_directory(void) { routerinfo_t *me = router_get_my_routerinfo(); - networkstatus_t *c; - routerstatus_t *rs; if (!me) return 0; if (!get_options()->HidServDirectoryV2) { @@ -5767,22 +5766,6 @@ hid_serv_acting_as_directory(void) "because we have not been configured as such."); return 0; } - if (!(c = networkstatus_get_latest_consensus())) { - log_info(LD_REND, "There's no consensus, so I can't tell if I'm a hidden " - "service directory"); - return 0; - } - rs = networkstatus_vote_find_entry(c, me->cache_info.identity_digest); - if (!rs) { - log_info(LD_REND, "We're not listed in the consensus, so we're not " - "being a hidden service directory."); - return 0; - } - if (!rs->is_hs_dir) { - log_info(LD_REND, "We're not listed as a hidden service directory in " - "the consensus, so we won't be one."); - return 0; - } return 1; } diff --git a/src/test/test_util.c b/src/test/test_util.c index 0da45df499..23cd059cf7 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -1195,6 +1195,35 @@ test_util_listdir(void *ptr) } } +static void +test_util_parent_dir(void *ptr) +{ + char *cp; + (void)ptr; + +#define T(input,expect_ok,output) \ + do { \ + int ok; \ + cp = tor_strdup(input); \ + ok = get_parent_directory(cp); \ + tt_int_op(ok, ==, expect_ok); \ + if (ok==0) \ + tt_str_op(cp, ==, output); \ + tor_free(cp); \ + } while (0); + + T("/home/wombat/knish", 0, "/home/wombat"); + T("/home/wombat/knish/", 0, "/home/wombat"); + T("./home/wombat/knish/", 0, "./home/wombat"); + T("./wombat", 0, "."); + T("", -1, ""); + T("/", -1, ""); + T("////", -1, ""); + + done: + tor_free(cp); +} + #ifdef MS_WINDOWS static void test_util_load_win_lib(void *ptr) @@ -1286,6 +1315,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(find_str_at_start_of_line, 0), UTIL_TEST(asprintf, 0), UTIL_TEST(listdir, 0), + UTIL_TEST(parent_dir, 0), #ifdef MS_WINDOWS UTIL_TEST(load_win_lib, 0), #endif |