From 907fc6c73ec7797361e53286e91c1bf21efa11d2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 Sep 2006 23:57:59 +0000 Subject: [PATCH] r8977@Kushana: nickm | 2006-09-28 19:56:41 -0400 Make "is a v1 authority", "is a v2 authority", and "is a hidden service authority" into separate flags so we can eventually migrate more trust away from moria. svn:r8523 --- ChangeLog | 4 +++ doc/TODO | 11 +++++++- doc/tor.1.in | 13 ++++++--- src/or/config.c | 31 +++++++++++++++++---- src/or/directory.c | 45 ++++++++++++++++++++---------- src/or/dirserv.c | 2 +- src/or/or.h | 16 +++++++++-- src/or/router.c | 6 ++-- src/or/routerlist.c | 67 +++++++++++++++++++++++++++++++-------------- 9 files changed, 143 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index 91d99252b6..123a55be10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,10 @@ Changes in version 0.1.2.2-alpha - 2006-??-?? directory authorities don't think it's a good guard, treat it as if it were unlisted: stop using it as a guard, and throw it off the guards list if it stays that way for a long time. + - Allow directory authorities to be marked separately as authorities for + the v1 directory protocol, the v2 directory protocol, and as hidden + service directories. This should make it easier to migrate trust away + from one of the two authorities currently running on Moria. o Security Fixes, minor: - If a client asked for a server by name, and we didn't have a diff --git a/doc/TODO b/doc/TODO index b3fe48b8a0..82c41af43a 100644 --- a/doc/TODO +++ b/doc/TODO @@ -35,13 +35,16 @@ x - If the client's clock is too far in the past, it will drop (or D The right thing here is to revamp our node selection implementation. (Deferred until oprofile says this matters.) o make it configurable, so people can turn it on or off. +N - Test guard unreachable logic; make sure that we actually attempt to + connect to guards that we think are unreachable from time to time. + Make sure that we don't freak out when the network is down. N - Clients stop dumping old descriptors if the network-statuses claim they're still valid. R . If we fail to connect via an exit enclave, (warn and) try again without demanding that exit node. - And recognize when extending to the enclave node is failing, so we can abandon then too. -N - We need a separate list of "hidserv authorities" if we want to + o We need a separate list of "hidserv authorities" if we want to retire moria1 from the main list. P - Figure out why dll's compiled in mingw don't work right in Winxp. P - Figure out why openssl 0.9.8c "make test" fails at sha256t test. @@ -58,6 +61,12 @@ R - Actually list all the events (notice and warn log messages are a good N - Specify general event system R - Specify actual events. +N - Have (and document) a BEGIN_DIR relay cell that means "Connect to your + directory port." + - Specify + - Implement + - Use for something, so we can be sure it works. + x - We should ship with a list of stable dir mirrors -- they're not trusted like the authorities, but they'll provide more robustness and diversity for bootstrapping clients. diff --git a/doc/tor.1.in b/doc/tor.1.in index c949ca92a8..1d03ba7d47 100644 --- a/doc/tor.1.in +++ b/doc/tor.1.in @@ -98,14 +98,19 @@ security. (Default: 0) Store working data in DIR (Default: @LOCALSTATEDIR@/lib/tor) .LP .TP -\fBDirServer \fR[\fInickname\fR] [\fBv1\fR] \fIaddress\fR\fB:\fIport fingerprint\fP +\fBDirServer \fR[\fInickname\fR] [\fBflags\fR] \fIaddress\fR\fB:\fIport fingerprint\fP Use a nonstandard authoritative directory server at the provided address and port, with the specified key fingerprint. This option can be repeated many times, for multiple authoritative directory -servers. If the "v1" option is provided, Tor will use this server as an +servers. Flags are separated by spaces, and determine what kind of an +authority this directory is. By default, every authority is authoritative +for current ("v2")-style directories, unless the "no-v2" flag is given. If the "v1" flags is provided, Tor will use this server as an authority for old-style (v1) directories as well. (Only directory mirrors -care about this.) If no \fBdirserver\fP line is given, Tor will use the default -directory servers: moria1, moria2, and tor26. NOTE: this option is intended +care about this.) Tor will use this server as an authority for hidden +service information if the "hs" flag is set, or if the "v1" flag is set and +the "no-hs" flag is \fBnot\fP set. +If no \fBdirserver\fP line is given, Tor will use the default +directory servers. NOTE: this option is intended for setting up a private Tor network with its own directory authorities. If you use it, you will be distinguishable from other users, because you won't believe the same authorities they do. diff --git a/src/or/config.c b/src/or/config.c index b77e733379..d785cae9aa 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -3244,7 +3244,8 @@ parse_dir_server_line(const char *line, int validate_only) char *addrport=NULL, *address=NULL, *nickname=NULL, *fingerprint=NULL; uint16_t port; char digest[DIGEST_LEN]; - int is_v1_authority = 0; + int is_v1_authority = 0, is_hidserv_authority = 0, + is_not_hidserv_authority = 0, is_v2_authority = 1; items = smartlist_create(); smartlist_split_string(items, line, NULL, @@ -3260,13 +3261,29 @@ parse_dir_server_line(const char *line, int validate_only) smartlist_del_keeporder(items, 0); } - if (!strcmp(smartlist_get(items, 0), "v1")) { - char *v1 = smartlist_get(items, 0); - tor_free(v1); - is_v1_authority = 1; + while (smartlist_len(items)) { + char *flag = smartlist_get(items, 0); + if (TOR_ISDIGIT(flag[0])) + break; + if (!strcasecmp(flag, "v1")) { + is_v1_authority = is_hidserv_authority = 1; + } else if (!strcasecmp(flag, "hs")) { + is_hidserv_authority = 1; + } else if (!strcasecmp(flag, "no-hs")) { + is_not_hidserv_authority = 1; + } else if (!strcasecmp(flag, "no-v2")) { + is_v2_authority = 0; + } else { + log_warn(LD_CONFIG, "Unrecognized flag '%s' on DirServer line", + flag); + } + tor_free(flag); smartlist_del_keeporder(items, 0); } + if (is_not_hidserv_authority) + is_hidserv_authority = 0; + if (smartlist_len(items) < 2) { log_warn(LD_CONFIG, "Too few arguments to DirServer line."); goto err; @@ -3295,7 +3312,9 @@ parse_dir_server_line(const char *line, int validate_only) if (!validate_only) { log_debug(LD_DIR, "Trusted dirserver at %s:%d (%s)", address, (int)port, (char*)smartlist_get(items,1)); - add_trusted_dir_server(nickname, address, port, digest, is_v1_authority); + add_trusted_dir_server(nickname, address, port, digest, is_v1_authority, + is_v2_authority, is_hidserv_authority); + } r = 0; diff --git a/src/or/directory.c b/src/or/directory.c index 90a110a5dd..7f5f7d78a1 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -90,19 +90,22 @@ directory_post_to_dirservers(uint8_t purpose, const char *payload, { smartlist_t *dirservers; int post_via_tor; - int post_to_v1_only; + int post_to_hidserv_only; dirservers = router_get_trusted_dir_servers(); tor_assert(dirservers); /* Only old dirservers handle rendezvous descriptor publishing. */ - post_to_v1_only = (purpose == DIR_PURPOSE_UPLOAD_RENDDESC); + post_to_hidserv_only = (purpose == DIR_PURPOSE_UPLOAD_RENDDESC); /* 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. */ SMARTLIST_FOREACH(dirservers, trusted_dir_server_t *, ds, { routerstatus_t *rs = &(ds->fake_status); - if (post_to_v1_only && !ds->is_v1_authority) + if (post_to_hidserv_only && !ds->is_hidserv_authority) + continue; + if (!post_to_hidserv_only && + !(ds->is_v1_authority || ds->is_v2_authority)) continue; post_via_tor = purpose_is_private(purpose) || !fascist_firewall_allows_address_dir(ds->addr, ds->dir_port); @@ -124,26 +127,37 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, or_options_t *options = get_options(); int prefer_authority = server_mode(options) && options->DirPort != 0; int directconn = !purpose_is_private(purpose); + authority_type_t type; - int need_v1_support = purpose == DIR_PURPOSE_FETCH_DIR || - purpose == DIR_PURPOSE_FETCH_RUNNING_LIST || - purpose == DIR_PURPOSE_FETCH_RENDDESC; - int need_v2_support = purpose == DIR_PURPOSE_FETCH_NETWORKSTATUS || - purpose == DIR_PURPOSE_FETCH_SERVERDESC; + switch (purpose) { + case DIR_PURPOSE_FETCH_NETWORKSTATUS: + case DIR_PURPOSE_FETCH_SERVERDESC: + type = V2_AUTHORITY; + break; + case DIR_PURPOSE_FETCH_DIR: + case DIR_PURPOSE_FETCH_RUNNING_LIST: + type = V1_AUTHORITY; + break; + case DIR_PURPOSE_FETCH_RENDDESC: + type = HIDSERV_AUTHORITY; + break; + default: + log_warn(LD_BUG, "Unexpected purpose %d", (int)purpose); + return; + } - if (!options->FetchServerDescriptors && - (need_v1_support || need_v2_support)) + if (!options->FetchServerDescriptors && type != HIDSERV_AUTHORITY) return; if (directconn) { if (prefer_authority) { /* only ask authdirservers, and don't ask myself */ - rs = router_pick_trusteddirserver(need_v1_support, 1, 1, + rs = router_pick_trusteddirserver(type, 1, 1, retry_if_no_servers); } if (!rs) { /* anybody with a non-zero dirport will do */ - rs = router_pick_directory_server(1, 1, need_v2_support, + rs = router_pick_directory_server(1, 1, type==V2_AUTHORITY, retry_if_no_servers); if (!rs) { const char *which; @@ -158,7 +172,7 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, log_info(LD_DIR, "No router found for %s; falling back to dirserver list", which); - rs = router_pick_trusteddirserver(need_v1_support, 1, 1, + rs = router_pick_trusteddirserver(type, 1, 1, retry_if_no_servers); if (!rs) directconn = 0; /* last resort: try routing it via Tor */ @@ -169,10 +183,11 @@ directory_get_from_dirserver(uint8_t purpose, const char *resource, /* Never use fascistfirewall; we're going via Tor. */ if (purpose == DIR_PURPOSE_FETCH_RENDDESC) { /* only ask authdirservers, any of them will do */ - rs = router_pick_trusteddirserver(1, 0, 0, retry_if_no_servers); + rs = router_pick_trusteddirserver(HIDSERV_AUTHORITY, 0, 0, + retry_if_no_servers); } else { /* anybody with a non-zero dirport will do. Disregard firewalls. */ - rs = router_pick_directory_server(1, 0, need_v2_support, + rs = router_pick_directory_server(1, 0, type == V2_AUTHORITY, retry_if_no_servers); /* If we have any hope of building an indirect conn, we know some router * descriptors. If (rs==NULL), we can't build circuits anyway, so diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 5a35b25139..18d44199ff 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1025,7 +1025,7 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus, digestmap_iter_get(iter, &ident, &val); d = val; if (d->published < oldest_published && - !router_get_trusteddirserver_by_digest(ident)) { + !router_digest_is_trusted_dir(ident)) { oldest = ident; oldest_published = d->published; } diff --git a/src/or/or.h b/src/or/or.h index 6c54a89a96..eb8f694b90 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2506,8 +2506,14 @@ typedef struct trusted_dir_server_t { char digest[DIGEST_LEN]; /**< Digest of identity key */ unsigned int is_running:1; /**< True iff we think this server is running. */ /** True iff this server is an authority for the older ("v1") directory - * protocol. (All authorities are v2 authorities.) */ + * protocol. */ unsigned int is_v1_authority:1; + /** True iff this server is an authority for the newer ("v2") directory + * protocol. */ + unsigned int is_v2_authority:1; + /** True iff this server is an authority for hidden services */ + unsigned int is_hidserv_authority:1; + int n_networkstatus_failures; /**< How many times have we asked for this * server's network-status unsuccessfully? */ routerstatus_t fake_status; /**< Used when we need to pass this trusted @@ -2524,7 +2530,10 @@ routerstatus_t *router_pick_directory_server(int requireother, int fascistfirewall, int for_v2_directory, int retry_if_no_servers); -routerstatus_t *router_pick_trusteddirserver(int need_v1_authority, +typedef enum { + V1_AUTHORITY, V2_AUTHORITY, HIDSERV_AUTHORITY, +} authority_type_t; +routerstatus_t *router_pick_trusteddirserver(authority_type_t type, int requireother, int fascistfirewall, int retry_if_no_servers); @@ -2592,7 +2601,8 @@ int router_exit_policy_rejects_all(routerinfo_t *router); void add_trusted_dir_server(const char *nickname, const char *address, uint16_t port, - const char *digest, int is_v1_authority); + const char *digest, int is_v1_authority, + int is_v2_authority, int is_hidserv_authority); void clear_trusted_dir_servers(void); int any_trusted_dir_is_v1_authority(void); networkstatus_t *networkstatus_get_by_digest(const char *digest); diff --git a/src/or/router.c b/src/or/router.c index 1f132607f7..4ead508fd6 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -361,8 +361,10 @@ init_keys(void) crypto_pk_get_digest(get_identity_key(), digest); if (!router_digest_is_trusted_dir(digest)) { add_trusted_dir_server(options->Nickname, NULL, - (uint16_t)options->DirPort, digest, - options->V1AuthoritativeDir); + (uint16_t)options->DirPort, digest, + options->V1AuthoritativeDir, /* v1 authority */ + 1, /* v2 authority */ + options->V1AuthoritativeDir /* hidserv authority */); } return 0; /* success */ } diff --git a/src/or/routerlist.c b/src/or/routerlist.c index eeda55068d..031a08fef6 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -22,7 +22,7 @@ static routerstatus_t *router_pick_directory_server_impl(int requireother, int fascistfirewall, int for_v2_directory); static routerstatus_t *router_pick_trusteddirserver_impl( - int need_v1_authority, int requireother, int fascistfirewall); + authority_type_t type, int requireother, int fascistfirewall); static void mark_all_trusteddirservers_up(void); static int router_nickname_matches(routerinfo_t *router, const char *nickname); static void routerstatus_list_update_from_networkstatus(time_t now); @@ -97,6 +97,19 @@ static int have_warned_about_old_version = 0; * listed by the authorities */ static int have_warned_about_new_version = 0; +/** Return the number of v2 directory authorities */ +static INLINE int +get_n_v2_authorities(void) +{ + int n = 0; + if (!trusted_dir_servers) + return 0; + SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds, + if (ds->is_v2_authority) + ++n); + return n; +} + /** Repopulate our list of network_status_t objects from the list cached on * disk. Return 0 on success, -1 on failure. */ int @@ -465,14 +478,14 @@ router_get_trusteddirserver_by_digest(const char *digest) * Other args are as in router_pick_trusteddirserver_impl(). */ routerstatus_t * -router_pick_trusteddirserver(int need_v1_authority, +router_pick_trusteddirserver(authority_type_t type, int requireother, int fascistfirewall, int retry_if_no_servers) { routerstatus_t *choice; - choice = router_pick_trusteddirserver_impl(need_v1_authority, + choice = router_pick_trusteddirserver_impl(type, requireother, fascistfirewall); if (choice || !retry_if_no_servers) return choice; @@ -480,7 +493,7 @@ router_pick_trusteddirserver(int need_v1_authority, log_info(LD_DIR, "No trusted dirservers are reachable. Trying them all again."); mark_all_trusteddirservers_up(); - return router_pick_trusteddirserver_impl(need_v1_authority, + return router_pick_trusteddirserver_impl(type, requireother, fascistfirewall); } @@ -540,7 +553,7 @@ router_pick_directory_server_impl(int requireother, int fascistfirewall, * system. */ static routerstatus_t * -router_pick_trusteddirserver_impl(int need_v1_authority, +router_pick_trusteddirserver_impl(authority_type_t type, int requireother, int fascistfirewall) { smartlist_t *sl; @@ -555,7 +568,11 @@ router_pick_trusteddirserver_impl(int need_v1_authority, SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, d, { if (!d->is_running) continue; - if (need_v1_authority && !d->is_v1_authority) + if (type == V1_AUTHORITY && !d->is_v1_authority) + continue; + if (type == V2_AUTHORITY && !d->is_v2_authority) + continue; + if (type == HIDSERV_AUTHORITY && !d->is_hidserv_authority) continue; if (requireother && me && router_digest_is_me(d->digest)) continue; @@ -1310,9 +1327,7 @@ dump_routerlist_mem_usage(int severity) static int max_descriptors_per_router(void) { - int n_authorities = 0; - if (trusted_dir_servers) - n_authorities = smartlist_len(trusted_dir_servers); + int n_authorities = get_n_v2_authorities(); return (n_authorities < 5) ? 5 : n_authorities; } @@ -2219,7 +2234,8 @@ router_set_networkstatus(const char *s, time_t arrived_at, } base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN); if (!(trusted_dir = - router_get_trusteddirserver_by_digest(ns->identity_digest))) { + router_get_trusteddirserver_by_digest(ns->identity_digest)) + || !trusted_dir->is_v2_authority) { log_info(LD_DIR, "Network status was signed, but not by an authoritative " "directory we recognize."); if (!get_options()->DirPort) { @@ -2536,6 +2552,8 @@ update_networkstatus_cache_downloads(time_t now) SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds, { char resource[HEX_DIGEST_LEN+6]; /* fp/hexdigit.z\0 */ + if (!ds->is_v2_authority) + continue; if (router_digest_is_me(ds->digest)) continue; if (connection_get_by_type_addr_port_purpose( @@ -2594,17 +2612,19 @@ update_networkstatus_client_downloads(time_t now) * *one* if the most recent one's publication time is under * NETWORKSTATUS_CLIENT_DL_INTERVAL. */ - if (!trusted_dir_servers || !smartlist_len(trusted_dir_servers)) + if (!get_n_v2_authorities()) return; - n_dirservers = n_running_dirservers = smartlist_len(trusted_dir_servers); + n_dirservers = n_running_dirservers = 0; missing = smartlist_create(); SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds, { networkstatus_t *ns = networkstatus_get_by_digest(ds->digest); - if (ds->n_networkstatus_failures > NETWORKSTATUS_N_ALLOWABLE_FAILURES) { - --n_running_dirservers; + if (!ds->is_v2_authority) continue; - } + ++n_dirservers; + if (ds->n_networkstatus_failures > NETWORKSTATUS_N_ALLOWABLE_FAILURES) + continue; + ++n_running_dirservers; if (ns && ns->published_on > now-NETWORKSTATUS_MAX_AGE) ++n_live; else @@ -2649,6 +2669,8 @@ update_networkstatus_client_downloads(time_t now) if (i >= n_dirservers) i = 0; ds = smartlist_get(trusted_dir_servers, i); + if (! ds->is_v2_authority) + continue; if (n_failed < n_dirservers && ds->n_networkstatus_failures > NETWORKSTATUS_N_ALLOWABLE_FAILURES) { ++n_failed; @@ -2726,7 +2748,8 @@ router_exit_policy_rejects_all(routerinfo_t *router) * address is NULL, add ourself. */ void add_trusted_dir_server(const char *nickname, const char *address, - uint16_t port, const char *digest, int is_v1_authority) + uint16_t port, const char *digest, int is_v1_authority, + int is_v2_authority, int is_hidserv_authority) { trusted_dir_server_t *ent; uint32_t a; @@ -2760,6 +2783,8 @@ add_trusted_dir_server(const char *nickname, const char *address, ent->dir_port = port; ent->is_running = 1; ent->is_v1_authority = is_v1_authority; + ent->is_v2_authority = is_v2_authority; + ent->is_hidserv_authority = is_hidserv_authority; memcpy(ent->digest, digest, DIGEST_LEN); dlen = 64 + strlen(hostname) + (nickname?strlen(nickname):0); @@ -3115,8 +3140,8 @@ routerstatus_list_update_from_networkstatus(time_t now) if (!warned_conflicts) warned_conflicts = smartlist_create(); - n_trusted = smartlist_len(trusted_dir_servers); n_statuses = smartlist_len(networkstatus_list); + n_trusted = get_n_v2_authorities(); if (n_statuses <= n_trusted/2) { /* Not enough statuses to adjust status. */ @@ -3620,8 +3645,8 @@ update_router_descriptor_client_downloads(time_t now) return; } - if (networkstatus_list && smartlist_len(networkstatus_list) <= - smartlist_len(trusted_dir_servers)/2) { + if (networkstatus_list && + smartlist_len(networkstatus_list) <= get_n_v2_authorities()/2) { log_info(LD_DIR, "Not enough networkstatus documents to launch requests."); } @@ -3859,7 +3884,7 @@ update_router_have_minimum_dir_info(void) res = 0; goto done; } - n_authorities = smartlist_len(trusted_dir_servers); + n_authorities = get_n_v2_authorities(); n_ns = smartlist_len(networkstatus_list); if (n_ns<=n_authorities/2) { log_info(LD_DIR, @@ -3904,6 +3929,8 @@ have_tried_downloading_all_statuses(int n_failures) SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds, { + if (!ds->is_v2_authority) + continue; /* If we don't have the status, and we haven't failed to get the status, * we haven't tried to get the status. */ if (!networkstatus_get_by_digest(ds->digest) &&