diff --git a/changes/ticket8240 b/changes/ticket8240 new file mode 100644 index 0000000000000000000000000000000000000000..91e6f8c14a930ee095663218db63a60a2bb26958 --- /dev/null +++ b/changes/ticket8240 @@ -0,0 +1,4 @@ + o Major security fixes: + - Make the default guard lifetime controllable via a new + GuardLifetime torrc option and a GuardLifetime consensus + parameter. Start of a fix for bug 8240; bugfix on 0.1.1.11-alpha. diff --git a/doc/tor.1.txt b/doc/tor.1.txt index b73d4a05dd9308a6f87329d1b6198559ac47584d..3be90be8703d3a94f03fcc5d0e3fbdc5c4a32e01 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1049,6 +1049,12 @@ The following options are useful only for clients (that is, if If UseEntryGuardsAsDirectoryGuards is enabled, we try to make sure we have at least NUM routers to use as directory guards. (Default: 3) +**GuardLifetime** __N__ **days**|**weeks**|**months**:: + If nonzero, and UseEntryGuards is set, minimum time to keep a guard before + picking a new one. If zero, we use the GuardLifetime parameter from the + consensus directory. No value here may be less than 1 month or greater + than 5 years; out-of-range values are clamped. (Default: 0) + **SafeSocks** **0**|**1**:: When this option is enabled, Tor will reject application connections that use unsafe variants of the socks protocol -- ones that only provide an IP diff --git a/src/common/util.h b/src/common/util.h index 4b923bf845f9cdd03e8353daea2ce59fe6dfe756..0dd6da3a5754c7aa0003c6f04478c79d20733d94 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -172,6 +172,17 @@ int n_bits_set_u8(uint8_t v); * overflow. */ #define CEIL_DIV(a,b) (((a)+(b)-1)/(b)) +/* Return <b>v</b> if it's between <b>min</b> and <b>max</b>. Otherwise + * return <b>min</b> if <b>v</b> is smaller than <b>min</b>, or <b>max</b> if + * <b>b</b> is larger than <b>max</b>. + * + * Requires that <b>min</b> is no more than <b>max</b>. May evaluate any of + * its arguments more than once! */ +#define CLAMP(min,v,max) \ + ( ((v) < (min)) ? (min) : \ + ((v) > (max)) ? (max) : \ + (v) ) + /* String manipulation */ /** Allowable characters in a hexadecimal string. */ diff --git a/src/or/config.c b/src/or/config.c index 861ee6ffeb2224e948c515c35c3952ba58a7f30c..86f06611469fd8ba0cf297bcbc960c1bf2b48f6c 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -255,6 +255,7 @@ static config_var_t option_vars_[] = { #endif OBSOLETE("GiveGuardFlagTo_CVE_2011_2768_VulnerableRelays"), OBSOLETE("Group"), + V(GuardLifetime, INTERVAL, "0 minutes"), V(HardwareAccel, BOOL, "0"), V(HeartbeatPeriod, INTERVAL, "6 hours"), V(AccelName, STRING, NULL), diff --git a/src/or/confparse.c b/src/or/confparse.c index 98fde98e7d0b8ac0d6c04e26a9b6a591ce047241..8863d924096c57510ba9a2a36d60ded0e4b7982b 100644 --- a/src/or/confparse.c +++ b/src/or/confparse.c @@ -1103,6 +1103,8 @@ static struct unit_table_t time_units[] = { { "days", 24*60*60 }, { "week", 7*24*60*60 }, { "weeks", 7*24*60*60 }, + { "month", 2629728, }, /* about 30.437 days */ + { "months", 2629728, }, { NULL, 0 }, }; diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c index 933eabbc00710a8b3bc672084d2a49b2c16013d2..b35e4d8ef3209d8db580ddaf60d5cd467cb080d4 100644 --- a/src/or/entrynodes.c +++ b/src/or/entrynodes.c @@ -24,6 +24,7 @@ #include "entrynodes.h" #include "main.h" #include "microdesc.h" +#include "networkstatus.h" #include "nodelist.h" #include "policies.h" #include "router.h" @@ -336,6 +337,9 @@ control_event_guard_deferred(void) #endif } +/** Largest amount that we'll backdate chosen_on_date */ +#define CHOSEN_ON_DATE_SLOP (30*86400) + /** Add a new (preferably stable and fast) router to our * entry_guards list. Return a pointer to the router if we succeed, * or NULL if we can't find any more suitable entries. @@ -449,6 +453,32 @@ entry_guard_free(entry_guard_t *e) tor_free(e); } +/** + * Return the minimum lifetime of working entry guard, in seconds, + * as given in the consensus networkstatus. (Plus CHOSEN_ON_DATE_SLOP, + * so that we can do the chosen_on_date randomization while achieving the + * desired minimum lifetime.) + */ +static int32_t +guards_get_lifetime(void) +{ + const or_options_t *options = get_options(); +#define DFLT_GUARD_LIFETIME (86400 * 30) /* One month. */ +#define MIN_GUARD_LIFETIME (86400 * 60) /* Two months. */ +#define MAX_GUARD_LIFETIME (86400 * 1826) /* Five years. */ + + if (options->GuardLifetime >= 1) { + return CLAMP(MIN_GUARD_LIFETIME, + options->GuardLifetime, + MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; + } + + return networkstatus_get_param(NULL, "GuardLifetime", + DFLT_GUARD_LIFETIME, + MIN_GUARD_LIFETIME, + MAX_GUARD_LIFETIME) + CHOSEN_ON_DATE_SLOP; +} + /** Remove any entry guard which was selected by an unknown version of Tor, * or which was selected by a version of Tor that's known to select * entry guards badly, or which was selected more 2 months ago. */ @@ -458,6 +488,7 @@ static int remove_obsolete_entry_guards(time_t now) { int changed = 0, i; + int32_t guard_lifetime = guards_get_lifetime(); for (i = 0; i < smartlist_len(entry_guards); ++i) { entry_guard_t *entry = smartlist_get(entry_guards, i); @@ -488,8 +519,8 @@ remove_obsolete_entry_guards(time_t now) } tor_free(tor_ver); } - if (!version_is_bad && entry->chosen_on_date + 3600*24*60 < now) { - /* It's been 2 months since the date listed in our state file. */ + if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) { + /* It's been too long since the date listed in our state file. */ msg = "was selected several months ago"; date_is_bad = 1; } diff --git a/src/or/or.h b/src/or/or.h index 68039afdf0476362547fdc3da966a78fae7fe51c..88fd38d9d79206586a9e81a69c54ec8c997ee9e1 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4029,6 +4029,8 @@ typedef struct { * should guess a suitable value. */ int SSLKeyLifetime; + /** How long (seconds) do we keep a guard before picking a new one? */ + int GuardLifetime; } or_options_t; /** Persistent state for an onion router, as saved to disk. */