From 6b83b3ba2aff4ba815241c76d712d149f13465e1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 12 May 2011 00:47:00 -0400 Subject: [PATCH 01/24] bug 3026: do not upload our vote to ourself --- changes/bug3026 | 4 ++++ src/or/directory.c | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 changes/bug3026 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/src/or/directory.c b/src/or/directory.c index 8c6581a123..86dcc8db1c 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 " From 7779c63e93c516c7a2fdfedc8ef73ccdb06e1df8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 12 May 2011 00:51:11 -0400 Subject: [PATCH 02/24] Accept hs descriptors even if we don't see an HSDir for us The old behavior contributed to unreliability when hidden services and hsdirs had different consensus versions, and so had different opinions about who should be cacheing hsdir info. Bugfix on 0.2.0.10-alpha; based on discussions surrounding bug 2732. --- changes/bug2732-simple | 7 +++++++ src/or/routerlist.c | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 changes/bug2732-simple 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/src/or/routerlist.c b/src/or/routerlist.c index f567ccdf3c..354e026306 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -5778,11 +5778,6 @@ hid_serv_acting_as_directory(void) "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; } From 2253697a04c926edb1a5f18727a6cd24528730b1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 16:18:53 -0400 Subject: [PATCH 03/24] New smartlist function to see if two lists of strings are equal. We'll use this to detect changes in CSV options. --- src/common/container.c | 19 +++++++++++++++++++ src/common/container.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/common/container.c b/src/common/container.c index 09d4bb131f..ca49cbb170 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -215,6 +215,25 @@ smartlist_string_num_isin(const smartlist_t *sl, int 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 sl has some element E such that * tor_memeq(E,element,DIGEST_LEN) */ diff --git a/src/common/container.h b/src/common/container.h index b39d4ca07e..f5e42de764 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) From 09da83e1e8c1f1050fd65af86736abc1f4e5b530 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 16:20:01 -0400 Subject: [PATCH 04/24] Don't clear out transient addressmap entries on HUP If you really want to purge the client DNS cache, the TrackHostExits mappings, and the virtual address mappings, you should be using NEWNYM instead. Fixes bug 1345; bugfix on Tor 0.1.0.1-rc. Note that this needs more work: now that we aren't nuking the transient addressmap entries on HUP, we need to make sure that configuration changes to VirtualAddressMap and TrackHostExits actually have a reasonable effect. --- changes/bug1345 | 4 ++++ src/or/main.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changes/bug1345 diff --git a/changes/bug1345 b/changes/bug1345 new file mode 100644 index 0000000000..6945b5fe17 --- /dev/null +++ b/changes/bug1345 @@ -0,0 +1,4 @@ + o Minor bugfixes: + - On SIGHUP, do not clear out all TrackExitHost 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. 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 */ From ec81d17d0c03d73f4d4acd9102893113b2883d76 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 16:22:10 -0400 Subject: [PATCH 05/24] Raise the TrackHostExits membership code into its own function --- src/or/circuituse.c | 35 ++++++++++++++++++++++------------- src/or/circuituse.h | 2 ++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index e68fb4fa82..6e069b1670 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1485,12 +1485,31 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ, } } +/** Return true iff address 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 specifically chosen, save the history for future * use. */ 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; @@ -1503,18 +1522,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, 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 From a3ae591115ba5c4a43ff4fa3839be274aac9e5c3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 16:22:58 -0400 Subject: [PATCH 06/24] When TrackExitHosts changes, remove all no-longer-valid mappings This bug couldn't happen when TrackExitHosts changed in torrc, since the SIGHUP to reload the torrc would clear out all the transient addressmap entries before. But if you used SETCONF to change TrackExitHosts, old entries would be left alone: that's a bug, and so this is a bugfix on Tor 0.1.0.1-rc. --- changes/bug1345 | 5 ++++- src/or/config.c | 10 +++++++++- src/or/connection_edge.c | 6 ++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/changes/bug1345 b/changes/bug1345 index 6945b5fe17..b35e78f5e8 100644 --- a/changes/bug1345 +++ b/changes/bug1345 @@ -1,4 +1,7 @@ o Minor bugfixes: - - On SIGHUP, do not clear out all TrackExitHost mappings, client DNS + - 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. diff --git a/src/or/config.c b/src/or/config.c index a7ff28f462..147cc66b6b 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1264,6 +1264,7 @@ options_act(or_options_t *old_options) /* Check for transitions that need action. */ if (old_options) { + int revise_trackexithosts = 0; if ((options->UseEntryGuards && !old_options->UseEntryGuards) || !routerset_equal(old_options->ExcludeNodes,options->ExcludeNodes) || !routerset_equal(old_options->ExcludeExitNodes, @@ -1276,9 +1277,16 @@ options_act(or_options_t *old_options) "excluded node lists. Abandoning previous circuits."); circuit_mark_all_unused_circs(); circuit_expire_all_dirty_circs(); - addressmap_clear_excluded_trackexithosts(options); + revise_trackexithosts = 1; } + if (!smartlist_strings_eq(old_options->TrackHostExits, + options->TrackHostExits)) + revise_trackexithosts = 1; + + if (revise_trackexithosts) + addressmap_clear_excluded_trackexithosts(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_edge.c b/src/or/connection_edge.c index 037920b688..5301471e91 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); From da8297dbcb6a44d2291878b01779500640e4d0b1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 16:59:31 -0400 Subject: [PATCH 07/24] Handle transitions in Automap*, VirtualAddrNetwork correctly Previously, if they changed in torrc during a SIGHUP, all was well, since we would just clear all transient entries from the addrmap thanks to bug 1345. But if you changed them from the controller, Tor would leave old mappings in place. The VirtualAddrNetwork bug has been here since 0.1.1.19-rc; the AutomapHosts* bug has been here since 0.2.0.1-alpha. --- changes/bug1345 | 6 ++++++ src/or/config.c | 15 ++++++++++++++ src/or/connection_edge.c | 45 +++++++++++++++++++++++++++++++++++++++- src/or/connection_edge.h | 1 + src/or/or.h | 3 +++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/changes/bug1345 b/changes/bug1345 index b35e78f5e8..0c9375a35d 100644 --- a/changes/bug1345 +++ b/changes/bug1345 @@ -5,3 +5,9 @@ - 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/src/or/config.c b/src/or/config.c index 147cc66b6b..9c68b6fa59 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1265,6 +1265,7 @@ 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, @@ -1287,6 +1288,20 @@ options_act(or_options_t *old_options) if (revise_trackexithosts) addressmap_clear_excluded_trackexithosts(options); + if (old_options->AutomapHostsOnResolve && !options->AutomapHostsOnResolve) { + revise_automap_entries = 1; + } else if (options->AutomapHostsOnResolve) { + 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_edge.c b/src/or/connection_edge.c index 5301471e91..7828f16386 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -861,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 @@ -1372,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/or.h b/src/or/or.h index a73d98ab74..5647691550 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -3150,6 +3150,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, From 1b512fb9146a4354067491d9d7de92f594a83390 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 11:14:28 -0400 Subject: [PATCH 08/24] Rip out more of hid_serv_acting_as_directory rransom notes correctly that now that we aren't checking our HSDir flag, we have no actual reason to check whether we are listed in the consensus at all when determining if we should act like a hidden service directory. --- src/or/routerlist.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 354e026306..22cf87e801 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -5758,8 +5758,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,17 +5765,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; - } return 1; } From 2bb6bdc3f9354a57b3daf0dbb494123cf39ba29f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 11:37:33 -0400 Subject: [PATCH 09/24] Better doc for consider_recording_trackexithost --- src/or/circuituse.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 6e069b1670..86db7c3a02 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1505,8 +1505,10 @@ hostname_in_track_host_exits(or_options_t *options, const char *address) return 0; } -/** If an exit wasn't specifically chosen, save the history for future - * use. */ +/** If an exit wasn't explicitly specified for conn, consider saving + * the exit that we *did* choose for use by future connections to + * conn's destination. + */ static void consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ) { From f2871009346e0589455be14e9cef930c19082c0a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 11:41:49 -0400 Subject: [PATCH 10/24] Replace a nasty add-malloc-snprintf with a nice clean asprintf --- src/or/circuituse.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 86db7c3a02..7289aa5c11 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -1513,8 +1513,7 @@ static void consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ) { 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. */ @@ -1534,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, From b48f83ab8c27b6a26d4afe5177238a99637a1dc1 Mon Sep 17 00:00:00 2001 From: Roger Dingledine Date: Sun, 15 May 2011 19:20:42 -0400 Subject: [PATCH 11/24] minor tweaks to 4b19730c8234d --- src/common/di_ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/di_ops.c b/src/common/di_ops.c index c1e292fe2f..3da033cf6d 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -3,7 +3,7 @@ /** * \file di_ops.c - * \brief Functions for data-independent operations + * \brief Functions for data-independent operations. **/ #include "orconfig.h" @@ -11,7 +11,7 @@ /** * Timing-safe version of memcmp. As memcmp, compare the sz bytes - * at a with the sz bytes at , and returns less than 0 if the + * at a with the sz bytes at , and return less than 0 if the * bytes at a lexically precede those at b, 0 if the byte ranges * are equal, and greater than zero if the bytes at a lexically follow * those at . From 10d670674aa49ee6764cf93fe7d3f485a3f9ffd8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 19:56:05 -0400 Subject: [PATCH 12/24] Another doc tweak on tor_memcmp: b, not . --- src/common/di_ops.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/common/di_ops.c b/src/common/di_ops.c index 3da033cf6d..b22a58d1b1 100644 --- a/src/common/di_ops.c +++ b/src/common/di_ops.c @@ -10,11 +10,11 @@ #include "di_ops.h" /** - * Timing-safe version of memcmp. As memcmp, compare the sz bytes - * at a with the sz bytes at , and return less than 0 if the - * bytes at a lexically precede those at b, 0 if the byte ranges - * are equal, and greater than zero if the bytes at a lexically follow - * those at . + * Timing-safe version of memcmp. As memcmp, compare the sz bytes at + * a with the sz bytes at b, and return less than 0 if + * the bytes at a lexically precede those at b, 0 if the byte + * ranges are equal, and greater than zero if the bytes at a lexically + * follow those at 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 sz bytes at - * a are the same as the sz bytes at , and 0 otherwise. + * a are the same as the sz bytes at 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 From 00ff80e0ae3a07ff3e2148d66d59716bc4630096 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 20:06:36 -0400 Subject: [PATCH 13/24] Fixup whitespace issues from 3122 commit --- src/common/container.h | 2 +- src/common/di_ops.h | 1 + src/or/dirvote.c | 10 ++++++---- src/or/networkstatus.c | 5 +++-- src/or/routerlist.c | 9 +++++---- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/common/container.h b/src/common/container.h index bb7cb89fc5..39619b0096 100644 --- a/src/common/container.h +++ b/src/common/container.h @@ -241,7 +241,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.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/or/dirvote.c b/src/or/dirvote.c index 9e763bdc91..f1381988c9 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -314,8 +314,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; @@ -823,7 +824,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; } }); @@ -881,7 +883,7 @@ networkstatus_compute_consensus(smartlist_t *votes, * routerinfo and its contents are. */ rs = compute_routerstatus_consensus(matching_descs); /* 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); diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index dcd8159aff..34d1e6ec98 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -1433,8 +1433,9 @@ networkstatus_set_current_consensus(const char *consensus, unsigned flags) } if (current_consensus && - tor_memeq(c->networkstatus_digest, current_consensus->networkstatus_digest, - DIGEST_LEN)) { + tor_memeq(c->networkstatus_digest, + current_consensus->networkstatus_digest, + DIGEST_LEN)) { /* We already have this one. That's a failure. */ log_info(LD_DIR, "Got a consensus we already have"); goto done; diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 9f04620986..be7ba0e965 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2008,8 +2008,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 @@ -4665,8 +4665,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 */ } From bc44393eb59a1d303314b4b646b4d815f4eadc22 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 20:12:01 -0400 Subject: [PATCH 14/24] Fixup whitespace issues from 3122 commit --- src/or/config.c | 7 ++++--- src/or/dirvote.c | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 9c68b6fa59..0997fe2697 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1288,9 +1288,10 @@ options_act(or_options_t *old_options) if (revise_trackexithosts) addressmap_clear_excluded_trackexithosts(options); - if (old_options->AutomapHostsOnResolve && !options->AutomapHostsOnResolve) { - revise_automap_entries = 1; - } else if (options->AutomapHostsOnResolve) { + 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; diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 517dac5074..96e3df5cec 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -2213,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."; From 2b9c5ee301f705cbf69c725ca749d4ac752c06d3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 12:46:26 -0400 Subject: [PATCH 15/24] Preserve bridge download status across SETCONF, HUP This code changes it so that we don't remove bridges immediately when we start re-parsing our configuration. Instead, we mark them all, and remove all the marked ones after re-parsing our bridge lines. As we add a bridge, we see if it's already in the list. If so, we just unmark it. This new behavior will lose the property we used to have that bridges were in bridge_list in the same order in which they appeared in the torrc. I took a quick look through the code, and I'm pretty sure we didn't actually depend on that anywhere. This is for bug 3019; it's a fix on 0.2.0.3-alpha. --- changes/bug3019 | 4 +++ src/or/circuitbuild.c | 57 +++++++++++++++++++++++++++++++++++++------ src/or/circuitbuild.h | 9 ++++--- src/or/config.c | 3 ++- 4 files changed, 61 insertions(+), 12 deletions(-) create mode 100644 changes/bug3019 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/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 addr:port. If digest - * 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/config.c b/src/or/config.c index 0997fe2697..6a2742d954 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -1172,7 +1172,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 +1180,7 @@ options_act(or_options_t *old_options) return -1; } } + sweep_bridge_list(); } if (running_tor && rend_config_services(options, 0)<0) { From d41ac64ad68811d8575db6e456fba470b0c3fde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Bobbio?= Date: Sat, 23 Apr 2011 02:35:02 +0200 Subject: [PATCH 16/24] Add UnixSocketsGroupWritable config flag When running a system-wide instance of Tor on Unix-like systems, having a ControlSocket is a quite handy mechanism to access Tor control channel. But it would be easier if access to the Unix domain socket can be granted by making control users members of the group running the Tor process. This change introduces a UnixSocketsGroupWritable option, which will create Unix domain sockets (and thus ControlSocket) 'g+rw'. This allows ControlSocket to offer same access control measures than ControlPort+CookieAuthFileGroupReadable. See for more details. --- doc/tor.1.txt | 5 +++++ src/or/config.c | 3 ++- src/or/connection.c | 7 +++++++ src/or/or.h | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index d95d764c67..d0d0c2f7cb 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.) +**UnixSocketsGroupWritable** **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/or/config.c b/src/or/config.c index 6a2742d954..c81fc9c594 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(UnixSocketsGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), @@ -952,7 +953,7 @@ options_act_reversible(or_options_t *old_options, char **msg) } #ifndef HAVE_SYS_UN_H - if (options->ControlSocket) { + if (options->ControlSocket || options->UnixSocketsGroupWritable) { *msg = tor_strdup("Unix domain sockets (ControlSocket) not supported" " on this OS/with this build."); goto rollback; diff --git a/src/or/connection.c b/src/or/connection.c index 01b533d9b5..d0898c5e5c 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -966,6 +966,13 @@ connection_create_listener(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } + if (get_options()->UnixSocketsGroupWritable) { + if (chmod(address, 0660) < 0) { + log_warn(LD_FS,"Unable to make %s group-readable.", 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/or.h b/src/or/or.h index 5647691550..b72693f029 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 UnixSocketsGroupWritable; /**< Boolean: Are unix 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. */ From 4198261291c8edbd5ba1617b7bfe3563e51edbe7 Mon Sep 17 00:00:00 2001 From: Sebastian Hahn Date: Sat, 23 Apr 2011 02:57:53 +0200 Subject: [PATCH 17/24] Clean up the 2972 implementation a little --- changes/bug2972 | 5 +++++ doc/tor.1.txt | 2 +- src/or/config.c | 14 ++++++++++---- src/or/connection.c | 4 ++-- src/or/or.h | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) create mode 100644 changes/bug2972 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/doc/tor.1.txt b/doc/tor.1.txt index d0d0c2f7cb..1815a8d963 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -167,7 +167,7 @@ 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.) -**UnixSocketsGroupWritable** **0**|**1**:: +**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) diff --git a/src/or/config.c b/src/or/config.c index c81fc9c594..614fc48c3e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -209,7 +209,7 @@ static config_var_t _option_vars[] = { V(ControlPortFileGroupReadable,BOOL, "0"), V(ControlPortWriteToFile, FILENAME, NULL), V(ControlSocket, LINELIST, NULL), - V(UnixSocketsGroupWritable, BOOL, "0"), + V(ControlSocketsGroupWritable, BOOL, "0"), V(CookieAuthentication, BOOL, "0"), V(CookieAuthFileGroupReadable, BOOL, "0"), V(CookieAuthFile, STRING, NULL), @@ -953,9 +953,15 @@ options_act_reversible(or_options_t *old_options, char **msg) } #ifndef HAVE_SYS_UN_H - if (options->ControlSocket || options->UnixSocketsGroupWritable) { - *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 diff --git a/src/or/connection.c b/src/or/connection.c index d0898c5e5c..12e00e59bb 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -966,9 +966,9 @@ connection_create_listener(const struct sockaddr *listensockaddr, tor_socket_strerror(tor_socket_errno(s))); goto err; } - if (get_options()->UnixSocketsGroupWritable) { + if (get_options()->ControlSocketsGroupWritable) { if (chmod(address, 0660) < 0) { - log_warn(LD_FS,"Unable to make %s group-readable.", address); + log_warn(LD_FS,"Unable to make %s group-writable.", address); tor_close_socket(s); goto err; } diff --git a/src/or/or.h b/src/or/or.h index b72693f029..b9d8319ba5 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2445,7 +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 UnixSocketsGroupWritable; /**< Boolean: Are unix sockets g+rw? */ + 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. */ From b147c01295deb8c09807fda3b5e4945edcfaf81a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 14:55:40 -0400 Subject: [PATCH 18/24] Make check_private_dir accept g+rx dirs if told to do so. --- src/common/util.c | 33 ++++++++++++++++++++++++--------- src/common/util.h | 6 +++++- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/src/common/util.c b/src/common/util.c index 86f4141674..3f81874331 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1665,16 +1665,22 @@ file_status(const char *fname) } /** 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 + * it does not exist, and check&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. */ + * check&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. + */ 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 +1692,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 +1704,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 +1734,18 @@ 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) { + mask = 0027; + } else { + mask = 0077; + } + if (st.st_mode & mask) { + unsigned new_mode; 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..f75953226b 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -286,7 +286,11 @@ 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 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) From 3b6cbf253494303f612eeb09a6fbb30a7c15c7fa Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 15:15:41 -0400 Subject: [PATCH 19/24] Add a function to pull off the final component of a path --- src/common/compat.c | 34 ++++++++++++++++++++++++++++++++++ src/common/compat.h | 2 ++ src/or/connection.c | 2 ++ src/test/test_util.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) diff --git a/src/common/compat.c b/src/common/compat.c index ea7f9d7efc..3c5e9385e2 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1467,6 +1467,40 @@ get_user_homedir(const char *username) } #endif +/** Modify fname 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 the final character that */ + 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/or/connection.c b/src/or/connection.c index 12e00e59bb..ab265df1af 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -967,6 +967,8 @@ connection_create_listener(const struct sockaddr *listensockaddr, 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); 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 From 5d147d8527da3c8cff7f5ab5f0d0185d51fff79b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 15:40:03 -0400 Subject: [PATCH 20/24] Add a new flag to check_private_dir to make it _not_ change permissions We'll need this for checking permissions on the directories that hold control sockets: if somebody says "ControlSocket ~/foo", it would be pretty rude to do a chmod 700 on their homedir. --- src/common/util.c | 7 +++++++ src/common/util.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/common/util.c b/src/common/util.c index 3f81874331..d84ed9c00e 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1670,6 +1670,8 @@ file_status(const char *fname) * check&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) @@ -1741,6 +1743,11 @@ check_private_dir(const char *dirname, cpd_check_t check) } 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); new_mode = st.st_mode; new_mode |= 0700; /* Owner should have rwx */ diff --git a/src/common/util.h b/src/common/util.h index f75953226b..f32709accd 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -291,6 +291,7 @@ typedef unsigned int cpd_check_t; #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) From 4b800408fa85ce0ac81a308c42d654b3357180d4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 13 May 2011 15:46:53 -0400 Subject: [PATCH 21/24] Check permissions on the directory holding a control socket --- changes/bug2792_checkdir | 8 ++++++++ src/or/connection.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 changes/bug2792_checkdir 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/src/or/connection.c b/src/or/connection.c index ab265df1af..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 + * path. 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 listensockaddr. * @@ -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); From 287f6cb128c890e31faa951be6d42cd6801f4e59 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 13:03:15 -0400 Subject: [PATCH 22/24] Fix up some comment issues spotted by rransom --- src/common/compat.c | 7 ++++++- src/common/util.c | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/common/compat.c b/src/common/compat.c index 3c5e9385e2..fc066da681 100644 --- a/src/common/compat.c +++ b/src/common/compat.c @@ -1481,7 +1481,12 @@ get_parent_directory(char *fname) fname += 2; } #endif - /* Now we want to remove the final character that */ + /* 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) { diff --git a/src/common/util.c b/src/common/util.c index d84ed9c00e..0e739f2127 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -1664,10 +1664,10 @@ 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 dirname exists and is private. If yes return 0. If + * it does not exist, and check&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 + * check&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 From f72e792be5437c9ee11d3f498ed3bb469b46d1bb Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 13:11:48 -0400 Subject: [PATCH 23/24] Make check_private_dir check for group ownership as appropriate --- src/common/util.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/common/util.c b/src/common/util.c index 0e739f2127..1bb116b212 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -30,6 +30,7 @@ #else #include #include +#include #endif /* math.h needs this on Linux */ @@ -1736,6 +1737,21 @@ check_private_dir(const char *dirname, cpd_check_t check) tor_free(process_ownername); return -1; } + 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(""); + 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 : "", (int)st.st_gid); + + tor_free(process_groupname); + return -1; + } if (check & CPD_GROUP_OK) { mask = 0027; } else { From 83fe07d3f26aff703230305c9ff529592af32d4f Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 15 May 2011 22:13:53 -0400 Subject: [PATCH 24/24] Increase the length of the buffer in smartlist_string_num_isin(). This was harmless, since we only used this for checking for lists of port values, but it's the principle of the thing. Fixes 3175; bugfix on 0.1.0.1-rc --- changes/bug3175 | 7 +++++++ src/common/container.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changes/bug3175 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/src/common/container.c b/src/common/container.c index ca49cbb170..da44b7fe68 100644 --- a/src/common/container.c +++ b/src/common/container.c @@ -210,7 +210,7 @@ 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); }