summaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/config.c44
-rw-r--r--src/or/config.h4
-rw-r--r--src/or/control.c43
-rw-r--r--src/or/control.h3
-rw-r--r--src/or/directory.c6
-rw-r--r--src/or/directory.h4
-rw-r--r--src/or/dirserv.c27
-rw-r--r--src/or/dirvote.c8
-rw-r--r--src/or/dirvote.h6
-rw-r--r--src/or/geoip.c9
-rw-r--r--src/or/geoip.h1
-rw-r--r--src/or/include.am4
-rw-r--r--src/or/keypin.c112
-rw-r--r--src/or/keypin.h3
-rw-r--r--src/or/main.c3
-rw-r--r--src/or/or.h10
-rw-r--r--src/or/policies.c212
-rw-r--r--src/or/policies.h12
-rw-r--r--src/or/rendcache.c136
-rw-r--r--src/or/rendcache.h12
-rw-r--r--src/or/rendcommon.c7
-rw-r--r--src/or/rendservice.c150
-rw-r--r--src/or/router.c6
-rw-r--r--src/or/router.h2
-rw-r--r--src/or/routerkeys.c92
-rw-r--r--src/or/routerkeys.h2
-rw-r--r--src/or/routerlist.c6
-rw-r--r--src/or/routerparse.c83
-rw-r--r--src/or/routerparse.h2
-rw-r--r--src/or/routerset.c23
-rw-r--r--src/or/statefile.c13
31 files changed, 838 insertions, 207 deletions
diff --git a/src/or/config.c b/src/or/config.c
index 98d9d83846..22039b46ef 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -162,6 +162,7 @@ static config_var_t option_vars_[] = {
V(AuthDirInvalidCCs, CSV, ""),
V(AuthDirFastGuarantee, MEMUNIT, "100 KB"),
V(AuthDirGuardBWGuarantee, MEMUNIT, "2 MB"),
+ V(AuthDirPinKeys, BOOL, "0"),
V(AuthDirReject, LINELIST, NULL),
V(AuthDirRejectCCs, CSV, ""),
OBSOLETE("AuthDirRejectUnlisted"),
@@ -311,6 +312,7 @@ static config_var_t option_vars_[] = {
V(LogMessageDomains, BOOL, "0"),
V(LogTimeGranularity, MSEC_INTERVAL, "1 second"),
V(TruncateLogFile, BOOL, "0"),
+ V(SyslogIdentityTag, STRING, NULL),
V(LongLivedPorts, CSV,
"21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"),
VAR("MapAddress", LINELIST, AddressMap, NULL),
@@ -333,6 +335,7 @@ static config_var_t option_vars_[] = {
V(NumCPUs, UINT, "0"),
V(NumDirectoryGuards, UINT, "0"),
V(NumEntryGuards, UINT, "0"),
+ V(OfflineMasterKey, BOOL, "0"),
V(ORListenAddress, LINELIST, NULL),
VPORT(ORPort, LINELIST, NULL),
V(OutboundBindAddress, LINELIST, NULL),
@@ -623,8 +626,8 @@ static char *global_dirfrontpagecontents = NULL;
static smartlist_t *configured_ports = NULL;
/** Return the contents of our frontpage string, or NULL if not configured. */
-const char *
-get_dirportfrontpage(void)
+MOCK_IMPL(const char*,
+get_dirportfrontpage, (void))
{
return global_dirfrontpagecontents;
}
@@ -762,6 +765,7 @@ or_options_free(or_options_t *options)
}
tor_free(options->BridgePassword_AuthDigest_);
tor_free(options->command_arg);
+ tor_free(options->master_key_fname);
config_free(&options_format, options);
}
@@ -1918,6 +1922,12 @@ static const struct {
{ "--dump-config", ARGUMENT_OPTIONAL },
{ "--list-fingerprint", TAKES_NO_ARGUMENT },
{ "--keygen", TAKES_NO_ARGUMENT },
+ { "--newpass", TAKES_NO_ARGUMENT },
+#if 0
+/* XXXX028: This is not working yet in 0.2.7, so disabling with the
+ * minimal code modification. */
+ { "--master-key", ARGUMENT_NECESSARY },
+#endif
{ "--no-passphrase", TAKES_NO_ARGUMENT },
{ "--passphrase-fd", ARGUMENT_NECESSARY },
{ "--verify-config", TAKES_NO_ARGUMENT },
@@ -3987,6 +3997,12 @@ options_transition_allowed(const or_options_t *old,
return -1;
}
+ if (!opt_streq(old->SyslogIdentityTag, new_val->SyslogIdentityTag)) {
+ *msg = tor_strdup("While Tor is running, changing "
+ "SyslogIdentityTag is not allowed.");
+ return -1;
+ }
+
if ((old->HardwareAccel != new_val->HardwareAccel)
|| !opt_streq(old->AccelName, new_val->AccelName)
|| !opt_streq(old->AccelDir, new_val->AccelDir)) {
@@ -4527,6 +4543,15 @@ options_init_from_torrc(int argc, char **argv)
}
}
+ if (config_line_find(cmdline_only_options, "--newpass")) {
+ if (command == CMD_KEYGEN) {
+ get_options_mutable()->change_key_passphrase = 1;
+ } else {
+ log_err(LD_CONFIG, "--newpass specified without --keygen!");
+ exit(1);
+ }
+ }
+
{
const config_line_t *fd_line = config_line_find(cmdline_only_options,
"--passphrase-fd");
@@ -4552,6 +4577,19 @@ options_init_from_torrc(int argc, char **argv)
}
}
+ {
+ const config_line_t *key_line = config_line_find(cmdline_only_options,
+ "--master-key");
+ if (key_line) {
+ if (command != CMD_KEYGEN) {
+ log_err(LD_CONFIG, "--master-key without --keygen!");
+ exit(1);
+ } else {
+ get_options_mutable()->master_key_fname = tor_strdup(key_line->value);
+ }
+ }
+ }
+
err:
tor_free(cf);
@@ -4906,7 +4944,7 @@ options_init_logs(const or_options_t *old_options, or_options_t *options,
!strcasecmp(smartlist_get(elts,0), "syslog")) {
#ifdef HAVE_SYSLOG_H
if (!validate_only) {
- add_syslog_log(severity);
+ add_syslog_log(severity, options->SyslogIdentityTag);
}
#else
log_warn(LD_CONFIG, "Syslog is not supported on this system. Sorry.");
diff --git a/src/or/config.h b/src/or/config.h
index 0ee1e1a3c4..51f7e90a2b 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -14,8 +14,8 @@
#include "testsupport.h"
-const char *get_dirportfrontpage(void);
-MOCK_DECL(const or_options_t *,get_options,(void));
+MOCK_DECL(const char*, get_dirportfrontpage, (void));
+MOCK_DECL(const or_options_t *, get_options, (void));
or_options_t *get_options_mutable(void);
int set_options(or_options_t *new_val, char **msg);
void config_free_all(void);
diff --git a/src/or/control.c b/src/or/control.c
index 220e7e514f..7d72342293 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -1927,6 +1927,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*errmsg = "Not found in cache";
return -1;
}
+ } else if (!strcmpstart(question, "hs/service/desc/id/")) {
+ rend_cache_entry_t *e = NULL;
+
+ question += strlen("hs/service/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (!rend_cache_lookup_v2_desc_as_service(question, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -2481,6 +2497,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
PREFIX("hs/client/desc/id", dir,
"Hidden Service descriptor in client's cache by onion."),
+ PREFIX("hs/service/desc/id/", dir,
+ "Hidden Service descriptor in services's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -6233,6 +6251,31 @@ get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
return desc_id;
}
+/** send HS_DESC CREATED event when a local service generates a descriptor.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>desc_id_base32</b> is the descriptor ID.
+ * <b>replica</b> is the the descriptor replica number.
+ */
+void
+control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica)
+{
+ if (!service_id || !desc_id_base32) {
+ log_warn(LD_BUG, "Called with service_digest==%p, "
+ "desc_id_base32==%p", service_id, desc_id_base32);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC,
+ "650 HS_DESC CREATED %s UNKNOWN UNKNOWN %s "
+ "REPLICA=%d\r\n",
+ service_id,
+ desc_id_base32,
+ replica);
+}
+
/** send HS_DESC upload event.
*
* <b>service_id</b> is the descriptor onion address.
diff --git a/src/or/control.h b/src/or/control.h
index fdf7903cb8..1f8e2bcdc6 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -117,6 +117,9 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
+void control_event_hs_descriptor_created(const char *service_id,
+ const char *desc_id_base32,
+ int replica);
void control_event_hs_descriptor_upload(const char *service_id,
const char *desc_id_base32,
const char *hs_dir);
diff --git a/src/or/directory.c b/src/or/directory.c
index 9461606f1b..6f8edb5ad4 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2614,7 +2614,7 @@ choose_compression_level(ssize_t n_bytes)
* service descriptor. On finding one, write a response into
* conn-\>outbuf. If the request is unrecognized, send a 400.
* Always return 0. */
-static int
+STATIC int
directory_handle_command_get(dir_connection_t *conn, const char *headers,
const char *req_body, size_t req_body_len)
{
@@ -2874,7 +2874,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
});
if (global_write_bucket_low(TO_CONN(conn), estimated_len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto vote_done;
}
write_http_response_header(conn, body_len ? body_len : -1, compressed,
@@ -3071,7 +3071,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
len += c->cache_info.signed_descriptor_len);
if (global_write_bucket_low(TO_CONN(conn), compressed?len/2:len, 2)) {
- write_http_status_line(conn, 503, "Directory busy, try again later.");
+ write_http_status_line(conn, 503, "Directory busy, try again later");
goto keys_done;
}
diff --git a/src/or/directory.h b/src/or/directory.h
index 4899eb5c8c..427183cac9 100644
--- a/src/or/directory.h
+++ b/src/or/directory.h
@@ -127,6 +127,10 @@ STATIC int purpose_needs_anonymity(uint8_t dir_purpose,
uint8_t router_purpose);
STATIC dirinfo_type_t dir_fetch_type(int dir_purpose, int router_purpose,
const char *resource);
+STATIC int directory_handle_command_get(dir_connection_t *conn,
+ const char *headers,
+ const char *req_body,
+ size_t req_body_len);
#endif
#endif
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 53b450cef5..8d9f166556 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -248,6 +248,7 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
int severity)
{
char d[DIGEST_LEN];
+ const int key_pinning = get_options()->AuthDirPinKeys;
if (crypto_pk_get_digest(router->identity_pkey, d)) {
log_warn(LD_BUG,"Error computing fingerprint");
@@ -261,14 +262,16 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
if (KEYPIN_MISMATCH ==
keypin_check((const uint8_t*)router->cache_info.identity_digest,
router->signing_key_cert->signing_key.pubkey)) {
- if (msg) {
- *msg = "Ed25519 identity key or RSA identity key has changed.";
- }
log_fn(severity, LD_DIR,
"Descriptor from router %s has an Ed25519 key, "
"but the <rsa,ed25519> keys don't match what they were before.",
router_describe(router));
- return FP_REJECT;
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key or RSA identity key has changed.";
+ }
+ return FP_REJECT;
+ }
}
} else {
/* No ed25519 key */
@@ -277,13 +280,15 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg,
log_fn(severity, LD_DIR,
"Descriptor from router %s has no Ed25519 key, "
"when we previously knew an Ed25519 for it. Ignoring for now, "
- "since Tor 0.2.6 is under development.",
+ "since Ed25519 keys are fairly new.",
router_describe(router));
#ifdef DISABLE_DISABLING_ED25519
- if (msg) {
- *msg = "Ed25519 identity key has disappeared.";
+ if (key_pinning) {
+ if (msg) {
+ *msg = "Ed25519 identity key has disappeared.";
+ }
+ return FP_REJECT;
}
- return FP_REJECT;
#endif
}
}
@@ -582,6 +587,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
char *desc, *nickname;
const size_t desclen = ri->cache_info.signed_descriptor_len +
ri->cache_info.annotations_len;
+ const int key_pinning = get_options()->AuthDirPinKeys;
*msg = NULL;
/* If it's too big, refuse it now. Otherwise we'll cache it all over the
@@ -626,7 +632,8 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
if (ri->signing_key_cert) {
keypin_status = keypin_check_and_add(
(const uint8_t*)ri->cache_info.identity_digest,
- ri->signing_key_cert->signing_key.pubkey);
+ ri->signing_key_cert->signing_key.pubkey,
+ ! key_pinning);
} else {
keypin_status = keypin_check_lone_rsa(
(const uint8_t*)ri->cache_info.identity_digest);
@@ -635,7 +642,7 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
keypin_status = KEYPIN_NOT_FOUND;
#endif
}
- if (keypin_status == KEYPIN_MISMATCH) {
+ if (keypin_status == KEYPIN_MISMATCH && key_pinning) {
log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
"its key did not match an older RSA/Ed25519 keypair",
router_describe(ri), source);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index d8e6ee2229..0449e9d8d9 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -3373,8 +3373,8 @@ dirvote_free_all(void)
* ==== */
/** Return the body of the consensus that we're currently trying to build. */
-const char *
-dirvote_get_pending_consensus(consensus_flavor_t flav)
+MOCK_IMPL(const char *,
+dirvote_get_pending_consensus, (consensus_flavor_t flav))
{
tor_assert(((int)flav) >= 0 && (int)flav < N_CONSENSUS_FLAVORS);
return pending_consensuses[flav].body;
@@ -3382,8 +3382,8 @@ dirvote_get_pending_consensus(consensus_flavor_t flav)
/** Return the signatures that we know for the consensus that we're currently
* trying to build. */
-const char *
-dirvote_get_pending_detached_signatures(void)
+MOCK_IMPL(const char *,
+dirvote_get_pending_detached_signatures, (void))
{
return pending_consensus_signatures;
}
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index dca8540870..966d163088 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -136,8 +136,10 @@ int dirvote_add_signatures(const char *detached_signatures_body,
const char **msg_out);
/* Item access */
-const char *dirvote_get_pending_consensus(consensus_flavor_t flav);
-const char *dirvote_get_pending_detached_signatures(void);
+MOCK_DECL(const char*, dirvote_get_pending_consensus,
+ (consensus_flavor_t flav));
+MOCK_DECL(const char*, dirvote_get_pending_detached_signatures, (void));
+
#define DGV_BY_ID 1
#define DGV_INCLUDE_PENDING 2
#define DGV_INCLUDE_PREVIOUS 4
diff --git a/src/or/geoip.c b/src/or/geoip.c
index 120ce479cc..e04c2484c0 100644
--- a/src/or/geoip.c
+++ b/src/or/geoip.c
@@ -18,7 +18,6 @@
#include "geoip.h"
#include "routerlist.h"
-static void clear_geoip_db(void);
static void init_geoip_countries(void);
/** An entry from the GeoIP IPv4 file: maps an IPv4 range to a country. */
@@ -1207,9 +1206,9 @@ geoip_format_dirreq_stats(time_t now)
{
char t[ISO_TIME_LEN+1];
int i;
- char *v3_ips_string, *v3_reqs_string, *v3_direct_dl_string,
- *v3_tunneled_dl_string;
- char *result;
+ char *v3_ips_string = NULL, *v3_reqs_string = NULL,
+ *v3_direct_dl_string = NULL, *v3_tunneled_dl_string = NULL;
+ char *result = NULL;
if (!start_of_dirreq_stats_interval)
return NULL; /* Not initialized. */
@@ -1666,7 +1665,7 @@ getinfo_helper_geoip(control_connection_t *control_conn,
}
/** Release all storage held by the GeoIP databases and country list. */
-static void
+STATIC void
clear_geoip_db(void)
{
if (geoip_countries) {
diff --git a/src/or/geoip.h b/src/or/geoip.h
index 8a3486c7ac..3f1bba01f8 100644
--- a/src/or/geoip.h
+++ b/src/or/geoip.h
@@ -18,6 +18,7 @@
STATIC int geoip_parse_entry(const char *line, sa_family_t family);
STATIC int geoip_get_country_by_ipv4(uint32_t ipaddr);
STATIC int geoip_get_country_by_ipv6(const struct in6_addr *addr);
+STATIC void clear_geoip_db(void);
#endif
int should_record_bridge_info(const or_options_t *options);
int geoip_load_file(sa_family_t family, const char *filename);
diff --git a/src/or/include.am b/src/or/include.am
index 7b12b56eb1..d0e955f495 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -123,9 +123,9 @@ src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
-export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov
+export TESTING_TOR_BINARY=$(top_builddir)/src/or/tor-cov
else
-export TESTING_TOR_BINARY = $(top_builddir)/src/or/tor
+export TESTING_TOR_BINARY=$(top_builddir)/src/or/tor
endif
ORHEADERS = \
diff --git a/src/or/keypin.c b/src/or/keypin.c
index ebe83b35d2..047d2b069b 100644
--- a/src/or/keypin.c
+++ b/src/or/keypin.c
@@ -48,7 +48,9 @@ static int keypin_journal_append_entry(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key);
static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key,
- int do_not_add);
+ const int do_not_add,
+ const int replace);
+static int keypin_add_or_replace_entry_in_map(keypin_ent_t *ent);
static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER();
static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER();
@@ -100,12 +102,17 @@ HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
* return KEYPIN_FOUND. If we find an entry that matches one key but
* not the other, return KEYPIN_MISMATCH. If we have no entry for either
* key, add such an entry to the table and return KEYPIN_ADDED.
+ *
+ * If <b>replace_existing_entry</b> is true, then any time we would have said
+ * KEYPIN_FOUND, we instead add this entry anyway and return KEYPIN_ADDED.
*/
int
keypin_check_and_add(const uint8_t *rsa_id_digest,
- const uint8_t *ed25519_id_key)
+ const uint8_t *ed25519_id_key,
+ const int replace_existing_entry)
{
- return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0);
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0,
+ replace_existing_entry);
}
/**
@@ -116,7 +123,7 @@ int
keypin_check(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key)
{
- return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1);
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1, 0);
}
/**
@@ -125,7 +132,8 @@ keypin_check(const uint8_t *rsa_id_digest,
static int
keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key,
- int do_not_add)
+ const int do_not_add,
+ const int replace)
{
keypin_ent_t search, *ent;
memset(&search, 0, sizeof(search));
@@ -139,18 +147,21 @@ keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) {
return KEYPIN_FOUND; /* Match on both keys. Great. */
} else {
- return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
+ if (!replace)
+ return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
}
}
/* See if we know a different RSA key for this ed key */
- ent = HT_FIND(edmap, &the_ed_map, &search);
- if (ent) {
- /* If we got here, then the ed key matches and the RSA doesn't */
- tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
- sizeof(ent->ed25519_key)));
- tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
- return KEYPIN_MISMATCH;
+ if (! replace) {
+ ent = HT_FIND(edmap, &the_ed_map, &search);
+ if (ent) {
+ /* If we got here, then the ed key matches and the RSA doesn't */
+ tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
+ sizeof(ent->ed25519_key)));
+ tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
+ return KEYPIN_MISMATCH;
+ }
}
/* Okay, this one is new to us. */
@@ -158,7 +169,12 @@ keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
return KEYPIN_NOT_FOUND;
ent = tor_memdup(&search, sizeof(search));
- keypin_add_entry_to_map(ent);
+ int r = keypin_add_or_replace_entry_in_map(ent);
+ if (! replace) {
+ tor_assert(r == 1);
+ } else {
+ tor_assert(r != 0);
+ }
keypin_journal_append_entry(rsa_id_digest, ed25519_id_key);
return KEYPIN_ADDED;
}
@@ -174,6 +190,57 @@ keypin_add_entry_to_map, (keypin_ent_t *ent))
}
/**
+ * Helper: add 'ent' to the maps, replacing any entries that contradict it.
+ * Take ownership of 'ent', freeing it if needed.
+ *
+ * Return 0 if the entry was a duplicate, -1 if there was a conflict,
+ * and 1 if there was no conflict.
+ */
+static int
+keypin_add_or_replace_entry_in_map(keypin_ent_t *ent)
+{
+ int r = 1;
+ keypin_ent_t *ent2 = HT_FIND(rsamap, &the_rsa_map, ent);
+ keypin_ent_t *ent3 = HT_FIND(edmap, &the_ed_map, ent);
+ if (ent2 &&
+ fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) {
+ /* We already have this mapping stored. Ignore it. */
+ tor_free(ent);
+ return 0;
+ } else if (ent2 || ent3) {
+ /* We have a conflict. (If we had no entry, we would have ent2 == ent3
+ * == NULL. If we had a non-conflicting duplicate, we would have found
+ * it above.)
+ *
+ * We respond by having this entry (ent) supersede all entries that it
+ * contradicts (ent2 and/or ent3). In other words, if we receive
+ * <rsa,ed>, we remove all <rsa,ed'> and all <rsa',ed>, for rsa'!=rsa
+ * and ed'!= ed.
+ */
+ const keypin_ent_t *t;
+ if (ent2) {
+ t = HT_REMOVE(rsamap, &the_rsa_map, ent2);
+ tor_assert(ent2 == t);
+ t = HT_REMOVE(edmap, &the_ed_map, ent2);
+ tor_assert(ent2 == t);
+ }
+ if (ent3 && ent2 != ent3) {
+ t = HT_REMOVE(rsamap, &the_rsa_map, ent3);
+ tor_assert(ent3 == t);
+ t = HT_REMOVE(edmap, &the_ed_map, ent3);
+ tor_assert(ent3 == t);
+ tor_free(ent3);
+ }
+ tor_free(ent2);
+ r = -1;
+ /* Fall through */
+ }
+
+ keypin_add_entry_to_map(ent);
+ return r;
+}
+
+/**
* Check whether we already have an entry in the key pinning table for a
* router with RSA ID digest <b>rsa_id_digest</b>. If we have no such entry,
* return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but
@@ -321,22 +388,13 @@ keypin_load_journal_impl(const char *data, size_t size)
continue;
}
- const keypin_ent_t *ent2;
- if ((ent2 = HT_FIND(rsamap, &the_rsa_map, ent))) {
- if (fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) {
- ++n_duplicates;
- } else {
- ++n_conflicts;
- }
- tor_free(ent);
- continue;
- } else if (HT_FIND(edmap, &the_ed_map, ent)) {
- tor_free(ent);
+ const int r = keypin_add_or_replace_entry_in_map(ent);
+ if (r == 0) {
+ ++n_duplicates;
+ } else if (r == -1) {
++n_conflicts;
- continue;
}
- keypin_add_entry_to_map(ent);
++n_entries;
}
diff --git a/src/or/keypin.h b/src/or/keypin.h
index 2a5b3f1786..798ac1fedb 100644
--- a/src/or/keypin.h
+++ b/src/or/keypin.h
@@ -7,7 +7,8 @@
#include "testsupport.h"
int keypin_check_and_add(const uint8_t *rsa_id_digest,
- const uint8_t *ed25519_id_key);
+ const uint8_t *ed25519_id_key,
+ const int replace_existing_entry);
int keypin_check(const uint8_t *rsa_id_digest,
const uint8_t *ed25519_id_key);
diff --git a/src/or/main.c b/src/or/main.c
index 693d13cd13..8f4c239567 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1518,7 +1518,8 @@ run_scheduled_events(time_t now)
/* Remove old information from rephist and the rend cache. */
if (time_to.clean_caches < now) {
rep_history_clean(now - options->RephistTrackTime);
- rend_cache_clean(now);
+ rend_cache_clean(now, REND_CACHE_TYPE_CLIENT);
+ rend_cache_clean(now, REND_CACHE_TYPE_SERVICE);
rend_cache_clean_v2_descs_as_dir(now, 0);
microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
diff --git a/src/or/or.h b/src/or/or.h
index 6660a0dcdc..651d8bed0c 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -3424,6 +3424,7 @@ typedef struct {
* each log message occurs? */
int TruncateLogFile; /**< Boolean: Should we truncate the log file
before we start writing? */
+ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */
char *DebugLogFile; /**< Where to send verbose log messages. */
char *DataDirectory; /**< OR only: where to store long-term data. */
@@ -3790,6 +3791,7 @@ typedef struct {
* number of servers per IP address shared
* with an authority. */
int AuthDirHasIPv6Connectivity; /**< Boolean: are we on IPv6? */
+ int AuthDirPinKeys; /**< Boolean: Do we enforce key-pinning? */
/** If non-zero, always vote the Fast flag for any relay advertising
* this amount of capacity or more. */
@@ -4013,7 +4015,7 @@ typedef struct {
char *ConsensusParams;
/** Authority only: minimum number of measured bandwidths we must see
- * before we only beliee measured bandwidths to assign flags. */
+ * before we only believe measured bandwidths to assign flags. */
int MinMeasuredBWsForAuthToIgnoreAdvertised;
/** The length of time that we think an initial consensus should be fresh.
@@ -4302,6 +4304,10 @@ typedef struct {
/** How long before auth keys expire will we try to make a new one? */
int TestingAuthKeySlop;
+ /** Force use of offline master key features: never generate a master
+ * ed25519 identity key except from tor --keygen */
+ int OfflineMasterKey;
+
enum {
FORCE_PASSPHRASE_AUTO=0,
FORCE_PASSPHRASE_ON,
@@ -4309,6 +4315,8 @@ typedef struct {
} keygen_force_passphrase;
int use_keygen_passphrase_fd;
int keygen_passphrase_fd;
+ int change_key_passphrase;
+ char *master_key_fname;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
diff --git a/src/or/policies.c b/src/or/policies.c
index 560b8cb4c3..9c858ec1b0 100644
--- a/src/or/policies.c
+++ b/src/or/policies.c
@@ -67,6 +67,8 @@ static int policies_parse_exit_policy_internal(config_line_t *cfg,
int ipv6_exit,
int rejectprivate,
uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses,
int add_default_policy);
/** Replace all "private" entries in *<b>policy</b> with their expanded
@@ -152,7 +154,7 @@ policy_expand_unspec(smartlist_t **policy)
}
/**
- * Given a linked list of config lines containing "allow" and "deny"
+ * Given a linked list of config lines containing "accept[6]" and "reject[6]"
* tokens, parse them and append the result to <b>dest</b>. Return -1
* if any tokens are malformed (and don't append any), else return 0.
*
@@ -167,6 +169,7 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
smartlist_t *result;
smartlist_t *entries;
addr_policy_t *item;
+ int malformed_list;
int r = 0;
if (!cfg)
@@ -179,12 +182,22 @@ parse_addr_policy(config_line_t *cfg, smartlist_t **dest,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(entries, const char *, ent) {
log_debug(LD_CONFIG,"Adding new entry '%s'",ent);
- item = router_parse_addr_policy_item_from_string(ent, assume_action);
+ malformed_list = 0;
+ item = router_parse_addr_policy_item_from_string(ent, assume_action,
+ &malformed_list);
if (item) {
smartlist_add(result, item);
- } else {
- log_warn(LD_CONFIG,"Malformed policy '%s'.", ent);
+ } else if (malformed_list) {
+ /* the error is so severe the entire list should be discarded */
+ log_warn(LD_CONFIG, "Malformed policy '%s'. Discarding entire policy "
+ "list.", ent);
r = -1;
+ } else {
+ /* the error is minor: don't add the item, but keep processing the
+ * rest of the policies in the list */
+ log_debug(LD_CONFIG, "Ignored policy '%s' due to non-fatal error. "
+ "The remainder of the policy list will be used.",
+ ent);
}
} SMARTLIST_FOREACH_END(ent);
SMARTLIST_FOREACH(entries, char *, ent, tor_free(ent));
@@ -430,7 +443,7 @@ validate_addr_policies(const or_options_t *options, char **msg)
smartlist_t *addr_policy=NULL;
*msg = NULL;
- if (policies_parse_exit_policy_from_options(options,0,&addr_policy)) {
+ if (policies_parse_exit_policy_from_options(options,0,NULL,0,&addr_policy)) {
REJECT("Error in ExitPolicy entry.");
}
@@ -568,6 +581,8 @@ cmp_single_addr_policy(addr_policy_t *a, addr_policy_t *b)
return r;
if ((r=((int)a->is_private - (int)b->is_private)))
return r;
+ /* refcnt and is_canonical are irrelevant to equality,
+ * they are hash table implementation details */
if ((r=tor_addr_compare(&a->addr, &b->addr, CMP_EXACT)))
return r;
if ((r=((int)a->maskbits - (int)b->maskbits)))
@@ -969,12 +984,24 @@ exit_policy_remove_redundancies(smartlist_t *dest)
"reject *:563,reject *:1214,reject *:4661-4666," \
"reject *:6346-6429,reject *:6699,reject *:6881-6999,accept *:*"
-/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>. If
- * cfg doesn't end in an absolute accept or reject and if
+/** Parse the exit policy <b>cfg</b> into the linked list *<b>dest</b>.
+ *
+ * If <b>ipv6_exit</b> is true, prepend "reject *6:*" to the policy.
+ *
+ * If <b>rejectprivate</b> is true:
+ * - prepend "reject private:*" to the policy.
+ * - if local_address is non-zero, treat it as a host-order IPv4 address,
+ * and prepend an entry that rejects it as a destination.
+ * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ * a destination.
+ * - if reject_interface_addresses is true, prepend entries that reject each
+ * public IPv4 and IPv6 address of each interface on this machine.
+ *
+ * If cfg doesn't end in an absolute accept or reject and if
* <b>add_default_policy</b> is true, add the default exit
- * policy afterwards. If <b>rejectprivate</b> is true, prepend
- * "reject private:*" to the policy. Return -1 if we can't parse cfg,
- * else return 0.
+ * policy afterwards.
+ *
+ * Return -1 if we can't parse cfg, else return 0.
*
* This function is used to parse the exit policy from our torrc. For
* the functions used to parse the exit policy from a router descriptor,
@@ -985,21 +1012,142 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
int ipv6_exit,
int rejectprivate,
uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses,
int add_default_policy)
{
if (!ipv6_exit) {
append_exit_policy_string(dest, "reject *6:*");
}
if (rejectprivate) {
+ /* Reject IPv4 and IPv6 reserved private netblocks */
append_exit_policy_string(dest, "reject private:*");
+ /* Reject our local IPv4 address */
if (local_address) {
char buf[POLICY_BUF_LEN];
tor_snprintf(buf, sizeof(buf), "reject %s:*", fmt_addr32(local_address));
append_exit_policy_string(dest, buf);
+ log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our published "
+ "IPv4 address", buf);
+ }
+ /* Reject our local IPv6 address */
+ if (ipv6_exit && ipv6_local_address != NULL) {
+ if (tor_addr_is_v4(ipv6_local_address)) {
+ log_warn(LD_CONFIG, "IPv4 address '%s' provided as our IPv6 local "
+ "address", fmt_addr(ipv6_local_address));
+ } else {
+ char buf6[POLICY_BUF_LEN];
+ tor_snprintf(buf6, sizeof(buf6), "reject [%s]:*",
+ fmt_addr(ipv6_local_address));
+ append_exit_policy_string(dest, buf6);
+ log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for our "
+ "published IPv6 address", buf6);
+ }
+ }
+ /* Reject local addresses from public netblocks on any interface,
+ * but don't reject our published addresses twice */
+ if (reject_interface_addresses) {
+ smartlist_t *public_addresses = NULL;
+ char bufif[POLICY_BUF_LEN];
+
+ /* Reject public IPv4 addresses on any interface,
+ * but don't reject our published IPv4 address twice */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET, 0);
+ SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+ if (!tor_addr_eq_ipv4h(a, local_address)) {
+ tor_snprintf(bufif, sizeof(bufif), "reject %s:*",
+ fmt_addr(a));
+ append_exit_policy_string(dest, bufif);
+ log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+ "interface's public IPv4 address", bufif);
+ }
+ } SMARTLIST_FOREACH_END(a);
+ free_interface_address6_list(public_addresses);
+
+ if (ipv6_exit) {
+ /* Reject public IPv6 addresses on any interface,
+ * but don't reject our published IPv6 address (if any) twice */
+ public_addresses = get_interface_address6_list(LOG_INFO, AF_INET6, 0);
+ SMARTLIST_FOREACH_BEGIN(public_addresses, tor_addr_t *, a) {
+ /* if we don't have an IPv6 local address, we won't have rejected
+ * it above. This could happen if a future release does IPv6
+ * autodiscovery, and we are waiting to discover our external IPv6
+ * address */
+ if (ipv6_local_address == NULL
+ || !tor_addr_eq(ipv6_local_address, a)) {
+ tor_snprintf(bufif, sizeof(bufif), "reject6 [%s]:*",
+ fmt_addr(a));
+ append_exit_policy_string(dest, bufif);
+ log_info(LD_CONFIG, "Adding a reject ExitPolicy '%s' for a local "
+ "interface's public IPv6 address", bufif);
+ }
+ } SMARTLIST_FOREACH_END(a);
+ free_interface_address6_list(public_addresses);
+ }
}
}
if (parse_addr_policy(cfg, dest, -1))
return -1;
+
+ /* Before we add the default policy and final rejects, check to see if
+ * there are any lines after accept *:* or reject *:*. These lines have no
+ * effect, and are most likely an error. */
+ int found_final_effective_entry = 0;
+ int first_redundant_entry = 0;
+ for (int i = 0; i < smartlist_len(*dest); ++i) {
+ sa_family_t family;
+ addr_policy_t *p;
+ int found_ipv4_wildcard = 0, found_ipv6_wildcard = 0;
+
+ p = smartlist_get(*dest, i);
+
+ /* Look for accept/reject *[4|6|]:* entires */
+ if (p->prt_min <= 1 && p->prt_max == 65535 && p->maskbits == 0) {
+ family = tor_addr_family(&p->addr);
+ /* accept/reject *:* may have already been expanded into
+ * accept/reject *4:*,accept/reject *6:*
+ * But handle both forms.
+ */
+ if (family == AF_INET || family == AF_UNSPEC) {
+ found_ipv4_wildcard = 1;
+ }
+ if (family == AF_INET6 || family == AF_UNSPEC) {
+ found_ipv6_wildcard = 1;
+ }
+ }
+
+ /* We also find accept *4:*,reject *6:* ; and
+ * accept *4:*,<other policies>,accept *6:* ; and similar.
+ * That's ok, because they make any subsequent entries redundant. */
+ if (found_ipv4_wildcard && found_ipv6_wildcard) {
+ found_final_effective_entry = 1;
+ /* if we're not on the final entry in the list */
+ if (i < smartlist_len(*dest) - 1) {
+ first_redundant_entry = i + 1;
+ }
+ break;
+ }
+ }
+ /* Work out if there are redundant trailing entries in the policy list */
+ if (found_final_effective_entry && first_redundant_entry > 0) {
+ addr_policy_t *p;
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 24 characters. */
+ char line[TOR_ADDR_BUF_LEN + 32];
+
+ tor_assert(first_redundant_entry < smartlist_len(*dest));
+ p = smartlist_get(*dest, first_redundant_entry);
+ /* since we've already parsed the policy into an addr_policy_t struct,
+ * we might not log exactly what the user typed in */
+ policy_write_item(line, TOR_ADDR_BUF_LEN + 32, p, 0);
+ log_warn(LD_DIR, "Exit policy '%s' and all following policies are "
+ "redundant, as it follows accept/reject *:* rules for both "
+ "IPv4 and IPv6. They will be removed from the exit policy. (Use "
+ "accept/reject *:* as the last entry in any exit policy.)",
+ line);
+ }
+
if (add_default_policy) {
append_exit_policy_string(dest, DEFAULT_EXIT_POLICY);
} else {
@@ -1013,20 +1161,28 @@ policies_parse_exit_policy_internal(config_line_t *cfg, smartlist_t **dest,
/** Parse exit policy in <b>cfg</b> into <b>dest</b> smartlist.
*
- * Add entry that rejects all IPv6 destinations unless
+ * Prepend an entry that rejects all IPv6 destinations unless
* <b>EXIT_POLICY_IPV6_ENABLED</b> bit is set in <b>options</b> bitmask.
*
- * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>,
- * do add entry that rejects all destinations in private subnetwork
- * Tor is running in.
+ * If <b>EXIT_POLICY_REJECT_PRIVATE</b> bit is set in <b>options</b>:
+ * - prepend an entry that rejects all destinations in all netblocks
+ * reserved for private use.
+ * - if local_address is non-zero, treat it as a host-order IPv4 address,
+ * and prepend an entry that rejects it as a destination.
+ * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as
+ * a destination.
+ * - if reject_interface_addresses is true, prepend entries that reject each
+ * public IPv4 and IPv6 address of each interface on this machine.
*
- * Respectively, if <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set, add
+ * If <b>EXIT_POLICY_ADD_DEFAULT</b> bit is set in <b>options</b>, append
* default exit policy entries to <b>result</b> smartlist.
*/
int
policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address)
+ uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses)
{
int ipv6_enabled = (options & EXIT_POLICY_IPV6_ENABLED) ? 1 : 0;
int reject_private = (options & EXIT_POLICY_REJECT_PRIVATE) ? 1 : 0;
@@ -1035,19 +1191,27 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
return policies_parse_exit_policy_internal(cfg,dest,ipv6_enabled,
reject_private,
local_address,
+ ipv6_local_address,
+ reject_interface_addresses,
add_default);
}
/** Parse <b>ExitPolicy</b> member of <b>or_options</b> into <b>result</b>
* smartlist.
- * If <b>or_options->IPv6Exit</b> is false, add an entry that
+ * If <b>or_options->IPv6Exit</b> is false, prepend an entry that
* rejects all IPv6 destinations.
*
- * If <b>or_options->ExitPolicyRejectPrivate</b> is true, add entry that
- * rejects all destinations in the private subnetwork of machine Tor
- * instance is running in.
+ * If <b>or_options->ExitPolicyRejectPrivate</b> is true:
+ * - prepend an entry that rejects all destinations in all netblocks reserved
+ * for private use.
+ * - if local_address is non-zero, treat it as a host-order IPv4 address, and
+ * prepend an entry that rejects it as a destination.
+ * - if ipv6_local_address is non-NULL, prepend an entry that rejects it as a
+ * destination.
+ * - if reject_interface_addresses is true, prepend entries that reject each
+ * public IPv4 and IPv6 address of each interface on this machine.
*
- * If <b>or_options->BridgeRelay</b> is false, add entries of default
+ * If <b>or_options->BridgeRelay</b> is false, append entries of default
* Tor exit policy into <b>result</b> smartlist.
*
* If or_options->ExitRelay is false, then make our exit policy into
@@ -1056,6 +1220,8 @@ policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
int
policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses,
smartlist_t **result)
{
exit_policy_parser_cfg_t parser_cfg = 0;
@@ -1079,7 +1245,9 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options,
}
return policies_parse_exit_policy(or_options->ExitPolicy,result,
- parser_cfg,local_address);
+ parser_cfg,local_address,
+ ipv6_local_address,
+ reject_interface_addresses);
}
/** Add "reject *:*" to the end of the policy in *<b>dest</b>, allocating
diff --git a/src/or/policies.h b/src/or/policies.h
index 0225b57a2c..f200d7babe 100644
--- a/src/or/policies.h
+++ b/src/or/policies.h
@@ -48,18 +48,16 @@ MOCK_DECL(addr_policy_result_t, compare_tor_addr_to_addr_policy,
addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr,
uint16_t port, const node_t *node);
-/*
-int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
- int ipv6exit,
- int rejectprivate, uint32_t local_address,
- int add_default_policy);
-*/
int policies_parse_exit_policy_from_options(const or_options_t *or_options,
uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses,
smartlist_t **result);
int policies_parse_exit_policy(config_line_t *cfg, smartlist_t **dest,
exit_policy_parser_cfg_t options,
- uint32_t local_address);
+ uint32_t local_address,
+ tor_addr_t *ipv6_local_address,
+ int reject_interface_addresses);
void policies_exit_policy_append_reject_star(smartlist_t **dest);
void addr_policy_append_reject_addr(smartlist_t **dest,
const tor_addr_t *addr);
diff --git a/src/or/rendcache.c b/src/or/rendcache.c
index 800cc2446b..fddedf165a 100644
--- a/src/or/rendcache.c
+++ b/src/or/rendcache.c
@@ -18,6 +18,9 @@
* rend_cache_entry_t. */
STATIC strmap_t *rend_cache = NULL;
+/** Map from service id to rend_cache_entry_t; only for hidden services. */
+static strmap_t *rend_cache_local_service = NULL;
+
/** Map from descriptor id to rend_cache_entry_t; only for hidden service
* directories. */
STATIC digestmap_t *rend_cache_v2_dir = NULL;
@@ -59,6 +62,7 @@ rend_cache_init(void)
{
rend_cache = strmap_new();
rend_cache_v2_dir = digestmap_new();
+ rend_cache_local_service = strmap_new();
rend_cache_failure = strmap_new();
}
@@ -219,9 +223,11 @@ rend_cache_free_all(void)
{
strmap_free(rend_cache, rend_cache_entry_free_);
digestmap_free(rend_cache_v2_dir, rend_cache_entry_free_);
+ strmap_free(rend_cache_local_service, rend_cache_entry_free_);
strmap_free(rend_cache_failure, rend_cache_failure_entry_free_);
rend_cache = NULL;
rend_cache_v2_dir = NULL;
+ rend_cache_local_service = NULL;
rend_cache_failure = NULL;
rend_cache_total_allocation = 0;
}
@@ -255,24 +261,33 @@ rend_cache_failure_clean(time_t now)
} STRMAP_FOREACH_END;
}
-/** Removes all old entries from the service descriptor cache.
+/** Removes all old entries from the client or service descriptor cache.
*/
void
-rend_cache_clean(time_t now)
+rend_cache_clean(time_t now, rend_cache_type_t cache_type)
{
strmap_iter_t *iter;
const char *key;
void *val;
rend_cache_entry_t *ent;
time_t cutoff = now - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
- for (iter = strmap_iter_init(rend_cache); !strmap_iter_done(iter); ) {
+ strmap_t *cache = NULL;
+
+ if (cache_type == REND_CACHE_TYPE_CLIENT) {
+ cache = rend_cache;
+ } else if (cache_type == REND_CACHE_TYPE_SERVICE) {
+ cache = rend_cache_local_service;
+ }
+ tor_assert(cache);
+
+ for (iter = strmap_iter_init(cache); !strmap_iter_done(iter); ) {
strmap_iter_get(iter, &key, &val);
ent = (rend_cache_entry_t*)val;
if (ent->parsed->timestamp < cutoff) {
- iter = strmap_iter_next_rmv(rend_cache, iter);
+ iter = strmap_iter_next_rmv(cache, iter);
rend_cache_entry_free(ent);
} else {
- iter = strmap_iter_next(rend_cache, iter);
+ iter = strmap_iter_next(cache, iter);
}
}
}
@@ -528,6 +543,42 @@ rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
return ret;
}
+/*
+ * Lookup the v2 service descriptor with the service ID <b>query</b> in the
+ * local service descriptor cache. Return 0 if found and if <b>e</b> is
+ * non NULL, set it with the entry found. Else, a negative value is returned
+ * and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
+int
+rend_cache_lookup_v2_desc_as_service(const char *query, rend_cache_entry_t **e)
+{
+ int ret = 0;
+ rend_cache_entry_t *entry = NULL;
+
+ tor_assert(rend_cache_local_service);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ /* Lookup descriptor and return. */
+ entry = strmap_get_lc(rend_cache_local_service, query);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
+ }
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
+}
+
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
* copy the pointer to it to *<b>desc</b>. Return 1 on success, 0 on
* well-formed-but-not-found, and -1 on failure.
@@ -679,6 +730,80 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
return RCS_OKAY;
}
+/** Parse the v2 service descriptor in <b>desc</b> and store it to the
+* local service rend cache. Don't attempt to decrypt the included list of
+* introduction points.
+*
+* If we have a newer descriptor with the same ID, ignore this one.
+* If we have an older descriptor with the same ID, replace it.
+*
+* Return an appropriate rend_cache_store_status_t.
+*/
+rend_cache_store_status_t
+rend_cache_store_v2_desc_as_service(const char *desc)
+{
+ rend_service_descriptor_t *parsed = NULL;
+ char desc_id[DIGEST_LEN];
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ char service_id[REND_SERVICE_ID_LEN_BASE32+1];
+ rend_cache_entry_t *e;
+ rend_cache_store_status_t retval = RCS_BADDESC;
+ tor_assert(rend_cache_local_service);
+ tor_assert(desc);
+
+ /* Parse the descriptor. */
+ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc, 0) < 0) {
+ log_warn(LD_REND, "Could not parse descriptor.");
+ goto err;
+ }
+ /* Compute service ID from public key. */
+ if (rend_get_service_id(parsed->pk, service_id)<0) {
+ log_warn(LD_REND, "Couldn't compute service ID.");
+ goto err;
+ }
+
+ /* Do we already have a newer descriptor? Allow new descriptors with a
+ rounded timestamp equal to or newer than the current descriptor */
+ e = (rend_cache_entry_t*) strmap_get_lc(rend_cache_local_service,
+ service_id);
+ if (e && e->parsed->timestamp > parsed->timestamp) {
+ log_info(LD_REND, "We already have a newer service descriptor for "
+ "service ID %s.", safe_str_client(service_id));
+ goto okay;
+ }
+ /* We don't care about the introduction points. */
+ tor_free(intro_content);
+ if (!e) {
+ e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+ strmap_set_lc(rend_cache_local_service, service_id, e);
+ } else {
+ rend_cache_decrement_allocation(rend_cache_entry_allocation(e));
+ rend_service_descriptor_free(e->parsed);
+ tor_free(e->desc);
+ }
+ e->parsed = parsed;
+ e->desc = tor_malloc_zero(encoded_size + 1);
+ strlcpy(e->desc, desc, encoded_size + 1);
+ e->len = encoded_size;
+ rend_cache_increment_allocation(rend_cache_entry_allocation(e));
+ log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+ safe_str_client(service_id), (int)encoded_size);
+ return RCS_OKAY;
+
+ okay:
+ retval = RCS_OKAY;
+
+ err:
+ rend_service_descriptor_free(parsed);
+ tor_free(intro_content);
+ return retval;
+}
+
/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
* of introduction points with <b>descriptor_cookie</b> (which may also be
* <b>NULL</b> if decryption is not necessary), and store the descriptor to
@@ -887,3 +1012,4 @@ rend_cache_store_v2_desc_as_client(const char *desc,
tor_free(intro_content);
return retval;
}
+
diff --git a/src/or/rendcache.h b/src/or/rendcache.h
index 4f55240750..a0cb68e08a 100644
--- a/src/or/rendcache.h
+++ b/src/or/rendcache.h
@@ -48,14 +48,21 @@ typedef struct rend_cache_failure_t {
digestmap_t *intro_failures;
} rend_cache_failure_t;
+typedef enum {
+ REND_CACHE_TYPE_CLIENT = 1,
+ REND_CACHE_TYPE_SERVICE = 2,
+} rend_cache_type_t;
+
void rend_cache_init(void);
-void rend_cache_clean(time_t now);
+void rend_cache_clean(time_t now, rend_cache_type_t cache_type);
void rend_cache_failure_clean(time_t now);
void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
+int rend_cache_lookup_v2_desc_as_service(const char *query,
+ rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
/** Return value from rend_cache_store_v2_desc_as_{dir,client}. */
typedef enum {
@@ -65,6 +72,8 @@ typedef enum {
} rend_cache_store_status_t;
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
+rend_cache_store_status_t rend_cache_store_v2_desc_as_service(
+ const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
const rend_data_t *rend_query,
@@ -101,3 +110,4 @@ STATIC void validate_intro_point_failure(const rend_service_descriptor_t *desc,
#endif
#endif /* TOR_RENDCACHE_H */
+
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 22599e9830..f9d47d13f5 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -11,6 +11,7 @@
#include "or.h"
#include "circuitbuild.h"
#include "config.h"
+#include "control.h"
#include "rendclient.h"
#include "rendcommon.h"
#include "rendmid.h"
@@ -461,6 +462,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
smartlist_t *client_cookies)
{
char service_id[DIGEST_LEN];
+ char service_id_base32[REND_SERVICE_ID_LEN_BASE32+1];
uint32_t time_period;
char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL,
*descriptor_cookie = NULL;
@@ -655,6 +657,11 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
goto err;
}
smartlist_add(descs_out, enc);
+ /* Add the uploaded descriptor to the local service's descriptor cache */
+ rend_cache_store_v2_desc_as_service(enc->desc_str);
+ base32_encode(service_id_base32, sizeof(service_id_base32),
+ service_id, REND_SERVICE_ID_LEN);
+ control_event_hs_descriptor_created(service_id_base32, desc_id_base32, k);
}
log_info(LD_REND, "Successfully encoded a v2 descriptor and "
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 8ba5327b1d..da65c6793a 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -3202,39 +3202,72 @@ upload_service_descriptor(rend_service_t *service)
rendpostperiod = get_options()->RendPostPeriod;
- /* Upload descriptor? */
- if (get_options()->PublishHidServDescriptors) {
- networkstatus_t *c = networkstatus_get_latest_consensus();
- if (c && smartlist_len(c->routerstatus_list) > 0) {
- int seconds_valid, i, j, num_descs;
- smartlist_t *descs = smartlist_new();
- smartlist_t *client_cookies = smartlist_new();
- /* Either upload a single descriptor (including replicas) or one
- * descriptor for each authorized client in case of authorization
- * type 'stealth'. */
- num_descs = service->auth_type == REND_STEALTH_AUTH ?
- smartlist_len(service->clients) : 1;
- for (j = 0; j < num_descs; j++) {
- crypto_pk_t *client_key = NULL;
- rend_authorized_client_t *client = NULL;
- smartlist_clear(client_cookies);
- switch (service->auth_type) {
- case REND_NO_AUTH:
- /* Do nothing here. */
- break;
- case REND_BASIC_AUTH:
- SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
- cl, smartlist_add(client_cookies, cl->descriptor_cookie));
- break;
- case REND_STEALTH_AUTH:
- client = smartlist_get(service->clients, j);
- client_key = client->client_key;
- smartlist_add(client_cookies, client->descriptor_cookie);
- break;
- }
- /* Encode the current descriptor. */
+ networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && smartlist_len(c->routerstatus_list) > 0) {
+ int seconds_valid, i, j, num_descs;
+ smartlist_t *descs = smartlist_new();
+ smartlist_t *client_cookies = smartlist_new();
+ /* Either upload a single descriptor (including replicas) or one
+ * descriptor for each authorized client in case of authorization
+ * type 'stealth'. */
+ num_descs = service->auth_type == REND_STEALTH_AUTH ?
+ smartlist_len(service->clients) : 1;
+ for (j = 0; j < num_descs; j++) {
+ crypto_pk_t *client_key = NULL;
+ rend_authorized_client_t *client = NULL;
+ smartlist_clear(client_cookies);
+ switch (service->auth_type) {
+ case REND_NO_AUTH:
+ /* Do nothing here. */
+ break;
+ case REND_BASIC_AUTH:
+ SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *,
+ cl, smartlist_add(client_cookies, cl->descriptor_cookie));
+ break;
+ case REND_STEALTH_AUTH:
+ client = smartlist_get(service->clients, j);
+ client_key = client->client_key;
+ smartlist_add(client_cookies, client->descriptor_cookie);
+ break;
+ }
+ /* Encode the current descriptor. */
+ seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
+ now, 0,
+ service->auth_type,
+ client_key,
+ client_cookies);
+ if (seconds_valid < 0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service "
+ "descriptor; not uploading.");
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ return;
+ }
+ rend_get_service_id(service->desc->pk, serviceid);
+ if (get_options()->PublishHidServDescriptors) {
+ /* Post the current descriptors to the hidden service directories. */
+ log_info(LD_REND, "Launching upload for hidden service %s",
+ serviceid);
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
+ /* Free memory for descriptors. */
+ for (i = 0; i < smartlist_len(descs); i++)
+ rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
+ smartlist_clear(descs);
+ /* Update next upload time. */
+ if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+ > rendpostperiod)
+ service->next_upload_time = now + rendpostperiod;
+ else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+ service->next_upload_time = now + seconds_valid + 1;
+ else
+ service->next_upload_time = now + seconds_valid -
+ REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+ /* Post also the next descriptors, if necessary. */
+ if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 0,
+ now, 1,
service->auth_type,
client_key,
client_cookies);
@@ -3245,51 +3278,23 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- /* Post the current descriptors to the hidden service directories. */
- rend_get_service_id(service->desc->pk, serviceid);
- log_info(LD_REND, "Launching upload for hidden service %s",
- serviceid);
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
+ if (get_options()->PublishHidServDescriptors) {
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
+ seconds_valid);
+ }
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
smartlist_clear(descs);
- /* Update next upload time. */
- if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
- > rendpostperiod)
- service->next_upload_time = now + rendpostperiod;
- else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
- service->next_upload_time = now + seconds_valid + 1;
- else
- service->next_upload_time = now + seconds_valid -
- REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
- /* Post also the next descriptors, if necessary. */
- if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
- seconds_valid = rend_encode_v2_descriptors(descs, service->desc,
- now, 1,
- service->auth_type,
- client_key,
- client_cookies);
- if (seconds_valid < 0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service "
- "descriptor; not uploading.");
- smartlist_free(descs);
- smartlist_free(client_cookies);
- return;
- }
- directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
- seconds_valid);
- /* Free memory for descriptors. */
- for (i = 0; i < smartlist_len(descs); i++)
- rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i));
- smartlist_clear(descs);
- }
}
- smartlist_free(descs);
- smartlist_free(client_cookies);
- uploaded = 1;
+ }
+ smartlist_free(descs);
+ smartlist_free(client_cookies);
+ uploaded = 1;
+ if (get_options()->PublishHidServDescriptors) {
log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+ } else {
+ log_info(LD_REND, "Successfully stored created v2 rend descriptors!");
}
}
@@ -3634,9 +3639,6 @@ rend_consider_services_upload(time_t now)
MIN_REND_INITIAL_POST_DELAY_TESTING :
MIN_REND_INITIAL_POST_DELAY);
- if (!get_options()->PublishHidServDescriptors)
- return;
-
for (i=0; i < smartlist_len(rend_service_list); ++i) {
service = smartlist_get(rend_service_list, i);
if (!service->next_upload_time) { /* never been uploaded yet */
diff --git a/src/or/router.c b/src/or/router.c
index 03973ae90a..9c6204c729 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -269,8 +269,8 @@ client_identity_key_is_set(void)
/** Return the key certificate for this v3 (voting) authority, or NULL
* if we have no such certificate. */
-authority_cert_t *
-get_my_v3_authority_cert(void)
+MOCK_IMPL(authority_cert_t *,
+get_my_v3_authority_cert, (void))
{
return authority_key_certificate;
}
@@ -1922,7 +1922,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
/* DNS is screwed up; don't claim to be an exit. */
policies_exit_policy_append_reject_star(&ri->exit_policy);
} else {
- policies_parse_exit_policy_from_options(options,ri->addr,
+ policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr,1,
&ri->exit_policy);
}
ri->policy_is_reject_star =
diff --git a/src/or/router.h b/src/or/router.h
index d8fcf0a9ad..2e8c02537b 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -22,7 +22,7 @@ int server_identity_key_is_set(void);
void set_client_identity_key(crypto_pk_t *k);
crypto_pk_t *get_tlsclient_identity_key(void);
int client_identity_key_is_set(void);
-authority_cert_t *get_my_v3_authority_cert(void);
+MOCK_DECL(authority_cert_t *, get_my_v3_authority_cert, (void));
crypto_pk_t *get_my_v3_authority_signing_key(void);
authority_cert_t *get_my_v3_legacy_cert(void);
crypto_pk_t *get_my_v3_legacy_signing_key(void);
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index f7c65c21b5..765dac883a 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -200,8 +200,17 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
{
if (encrypted) {
int r = write_encrypted_secret_key(key, encrypted_fname);
- if (r != 0)
- return r; /* Either succeeded or failed unrecoverably */
+ if (r == 1) {
+ /* Success! */
+
+ /* Try to unlink the unencrypted key, if any existed before */
+ if (strcmp(fname, encrypted_fname))
+ unlink(fname);
+ return r;
+ } else if (r != 0) {
+ /* Unrecoverable failure! */
+ return r;
+ }
fprintf(stderr, "Not encrypting the secret key.\n");
}
@@ -237,8 +246,12 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
* If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a
* public key file but no secret key file, return successfully anyway.
*
- * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not even try to
- * load or return a secret key (but create and save one if needed).
+ * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not try to load a
+ * secret key unless no public key is found. Do not return a secret key. (but
+ * create and save one if needed).
+ *
+ * If INIT_ED_KEY_NO_LOAD_SECRET is set in <b>flags</b>, don't try to load
+ * a secret key, no matter what.
*
* If INIT_ED_KEY_TRY_ENCRYPTED is set, we look for an encrypted secret key
* and consider encrypting any new secret key.
@@ -249,6 +262,9 @@ write_secret_key(const ed25519_secret_key_t *key, int encrypted,
*
* If INIT_ED_KEY_SUGGEST_KEYGEN is set, have log messages about failures
* refer to the --keygen option.
+ *
+ * If INIT_ED_KEY_EXPLICIT_FNAME is set, use the provided file name for the
+ * secret key file, encrypted or not.
*/
ed25519_keypair_t *
ed_key_init_from_file(const char *fname, uint32_t flags,
@@ -269,7 +285,9 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
const int encrypt_key = !! (flags & INIT_ED_KEY_TRY_ENCRYPTED);
const int norepair = !! (flags & INIT_ED_KEY_NO_REPAIR);
const int split = !! (flags & INIT_ED_KEY_SPLIT);
- const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET);
+ const int omit_secret = !! (flags & INIT_ED_KEY_OMIT_SECRET);
+ const int offline_secret = !! (flags & INIT_ED_KEY_OFFLINE_SECRET);
+ const int explicit_fname = !! (flags & INIT_ED_KEY_EXPLICIT_FNAME);
/* we don't support setting both of these flags at once. */
tor_assert((flags & (INIT_ED_KEY_NO_REPAIR|INIT_ED_KEY_NEEDCERT)) !=
@@ -282,14 +300,22 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
char *got_tag = NULL;
ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));
- tor_asprintf(&secret_fname, "%s_secret_key", fname);
- tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname);
+ if (explicit_fname) {
+ secret_fname = tor_strdup(fname);
+ encrypted_secret_fname = tor_strdup(fname);
+ } else {
+ tor_asprintf(&secret_fname, "%s_secret_key", fname);
+ tor_asprintf(&encrypted_secret_fname, "%s_secret_key_encrypted", fname);
+ }
tor_asprintf(&public_fname, "%s_public_key", fname);
tor_asprintf(&cert_fname, "%s_cert", fname);
/* Try to read the secret key. */
int have_secret = 0;
- if (try_to_load && (!omit_secret || file_status(public_fname)==FN_NOENT )) {
+ int load_secret = try_to_load &&
+ !offline_secret &&
+ (!omit_secret || file_status(public_fname)==FN_NOENT);
+ if (load_secret) {
int rv = ed25519_seckey_read_from_file(&keypair->seckey,
&got_tag, secret_fname);
if (rv == 0) {
@@ -433,7 +459,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
goto err;
}
- /* if it's absent, make a new keypair and save it. */
+ /* if it's absent, make a new keypair... */
if (!have_secret && !found_public) {
tor_free(keypair);
keypair = ed_key_new(signing_key, flags, now, lifetime,
@@ -442,8 +468,12 @@ ed_key_init_from_file(const char *fname, uint32_t flags,
tor_log(severity, LD_OR, "Couldn't create keypair");
goto err;
}
-
created_pk = created_sk = created_cert = 1;
+ }
+
+ /* Write it to disk if we're supposed to do with a new passphrase, or if
+ * we just created it. */
+ if (created_sk || (have_secret && get_options()->change_key_passphrase)) {
if (write_secret_key(&keypair->seckey,
encrypt_key,
secret_fname, tag, encrypted_secret_fname) < 0
@@ -673,38 +703,56 @@ load_ed_keys(const or_options_t *options, time_t now)
use_signing = master_signing_key;
}
+ const int offline_master =
+ options->OfflineMasterKey && options->command != CMD_KEYGEN;
const int need_new_signing_key =
NULL == use_signing ||
EXPIRES_SOON(check_signing_cert, 0) ||
- options->command == CMD_KEYGEN;
+ (options->command == CMD_KEYGEN && ! options->change_key_passphrase);
const int want_new_signing_key =
need_new_signing_key ||
EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop);
+ /* We can only create a master key if we haven't been told that the
+ * master key will always be offline. Also, if we have a signing key,
+ * then we shouldn't make a new master ID key. */
+ const int can_make_master_id_key = !offline_master &&
+ NULL == use_signing;
+
if (need_new_signing_key) {
log_notice(LD_OR, "It looks like I need to generate and sign a new "
"medium-term signing key, because %s. To do that, I need to "
- "load (or create) the permanent master identity key.",
+ "load%s the permanent master identity key.",
(NULL == use_signing) ? "I don't have one" :
EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" :
- "you asked me to make one with --keygen");
- } else if (want_new_signing_key) {
+ "you asked me to make one with --keygen",
+ can_make_master_id_key ? " (or create)" : "");
+ } else if (want_new_signing_key && !offline_master) {
log_notice(LD_OR, "It looks like I should try to generate and sign a "
"new medium-term signing key, because the one I have is "
"going to expire soon. To do that, I'm going to have to try to "
"load the permanent master identity key.");
+ } else if (want_new_signing_key) {
+ log_notice(LD_OR, "It looks like I should try to generate and sign a "
+ "new medium-term signing key, because the one I have is "
+ "going to expire soon. But OfflineMasterKey is set, so I "
+ "won't try to load a permanent master identity key is set. "
+ "You will need to use 'tor --keygen' make a new signing key "
+ "and certificate.");
}
{
uint32_t flags =
(INIT_ED_KEY_SPLIT|
INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR);
- if (! use_signing)
+ if (can_make_master_id_key)
flags |= INIT_ED_KEY_CREATE;
if (! need_new_signing_key)
flags |= INIT_ED_KEY_MISSING_SECRET_OK;
- if (! want_new_signing_key)
+ if (! want_new_signing_key || offline_master)
flags |= INIT_ED_KEY_OMIT_SECRET;
+ if (offline_master)
+ flags |= INIT_ED_KEY_OFFLINE_SECRET;
if (options->command == CMD_KEYGEN)
flags |= INIT_ED_KEY_TRY_ENCRYPTED;
@@ -721,7 +769,12 @@ load_ed_keys(const or_options_t *options, time_t now)
goto err;
}
tor_free(fname);
- fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ if (options->master_key_fname) {
+ fname = tor_strdup(options->master_key_fname);
+ flags |= INIT_ED_KEY_EXPLICIT_FNAME;
+ } else {
+ fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ }
id = ed_key_init_from_file(
fname,
flags,
@@ -729,7 +782,10 @@ load_ed_keys(const or_options_t *options, time_t now)
tor_free(fname);
if (!id) {
if (need_new_signing_key) {
- FAIL("Missing identity key");
+ if (offline_master)
+ FAIL("Can't load master identity key; OfflineMasterKey is set.");
+ else
+ FAIL("Missing identity key");
} else {
log_warn(LD_OR, "Master public key was absent; inferring from "
"public key in signing certificate and saving to disk.");
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
index b4e73aa33f..0e1c62571f 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -17,6 +17,8 @@
#define INIT_ED_KEY_TRY_ENCRYPTED (1u<<8)
#define INIT_ED_KEY_NO_REPAIR (1u<<9)
#define INIT_ED_KEY_SUGGEST_KEYGEN (1u<<10)
+#define INIT_ED_KEY_OFFLINE_SECRET (1u<<11)
+#define INIT_ED_KEY_EXPLICIT_FNAME (1u<<12)
struct tor_cert_st;
ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags,
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 93dc2fe9d2..2669977d1a 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -4964,6 +4964,12 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
goto err;
}
+ if (!digest256_matches && !digest_matches) {
+ if (msg) *msg = "Neither digest256 or digest matched "
+ "digest from routerdesc";
+ goto err;
+ }
+
if (!digest256_matches) {
if (msg) *msg = "Extrainfo digest did not match digest256 from routerdesc";
goto err; /* Digest doesn't match declared value. */
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index c2206f1075..f898ef8aef 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -3666,24 +3666,38 @@ networkstatus_parse_detached_signatures(const char *s, const char *eos)
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action.
*
+ * Returns NULL on policy errors.
+ *
+ * If there is a policy error, malformed_list is set to true if the entire
+ * policy list should be discarded. Otherwise, it is set to false, and only
+ * this item should be ignored - the rest of the policy list can continue to
+ * be processed and used.
+ *
* The addr_policy_t returned by this function can have its address set to
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
* of AF_INET and AF_INET6 items.
*/
MOCK_IMPL(addr_policy_t *,
-router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
+router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
+ int *malformed_list))
{
directory_token_t *tok = NULL;
const char *cp, *eos;
/* Longest possible policy is
- * "accept6 ffff:ffff:..255/ffff:...255:10000-65535",
- * which contains 2 max-length IPv6 addresses, plus 21 characters.
+ * "accept6 ffff:ffff:..255/128:10000-65535",
+ * which contains a max-length IPv6 address, plus 24 characters.
* But note that there can be an arbitrary amount of space between the
- * accept and the address:mask/port element. */
+ * accept and the address:mask/port element.
+ * We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
+ * IPv6 address. But making the buffer shorter might cause valid long lines,
+ * which parsed in previous versions, to fail to parse in new versions.
+ * (These lines would have to have excessive amounts of whitespace.) */
char line[TOR_ADDR_BUF_LEN*2 + 32];
addr_policy_t *r;
memarea_t *area = NULL;
+ tor_assert(malformed_list);
+
s = eat_whitespace(s);
if ((*s == '*' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
if (tor_snprintf(line, sizeof(line), "%s %s",
@@ -3710,9 +3724,34 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
goto err;
}
+ /* Use the extended interpretation of accept/reject *,
+ * expanding it into an IPv4 wildcard and an IPv6 wildcard.
+ * Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */
r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
+ if (!r) {
+ goto err;
+ }
+
+ /* Ensure that accept6/reject6 fields are followed by IPv6 addresses.
+ * AF_UNSPEC addresses are only permitted on the accept/reject field type.
+ * Unlike descriptors, torrcs exit policy accept/reject can be followed by
+ * either an IPv4 or IPv6 address. */
+ if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
+ tor_addr_family(&r->addr) != AF_INET6) {
+ /* This is a non-fatal error, just ignore this one entry. */
+ *malformed_list = 0;
+ log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in "
+ "exit policy. Ignoring, but continuing to parse rules. (Use "
+ "accept/reject with IPv4 addresses.)",
+ tok->n_args == 1 ? tok->args[0] : "");
+ addr_policy_free(r);
+ r = NULL;
+ goto done;
+ }
+
goto done;
err:
+ *malformed_list = 1;
r = NULL;
done:
token_clear(tok);
@@ -3729,19 +3768,27 @@ static int
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
{
addr_policy_t *newe;
+ /* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */
newe = router_parse_addr_policy(tok, 0);
if (!newe)
return -1;
if (! router->exit_policy)
router->exit_policy = smartlist_new();
+ /* Ensure that in descriptors, accept/reject fields are followed by
+ * IPv4 addresses, and accept6/reject6 fields are followed by
+ * IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit
+ * accept/reject followed by IPv6. */
if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
tor_addr_family(&newe->addr) == AF_INET)
||
((tok->tp == K_ACCEPT || tok->tp == K_REJECT) &&
tor_addr_family(&newe->addr) == AF_INET6)) {
+ /* There's nothing the user can do about other relays' descriptors,
+ * so we don't provide usage advice here. */
log_warn(LD_DIR, "Mismatch between field type and address type in exit "
- "policy");
+ "policy '%s'. Discarding entire router descriptor.",
+ tok->n_args == 1 ? tok->args[0] : "");
addr_policy_free(newe);
return -1;
}
@@ -3751,8 +3798,11 @@ router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
return 0;
}
-/** Given a K_ACCEPT or K_REJECT token and a router, create and return
- * a new exit_policy_t corresponding to the token. */
+/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return
+ * a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR
+ * is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by *
+ * expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6
+ * policies */
static addr_policy_t *
router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
{
@@ -3776,6 +3826,13 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
else
newe.policy_type = ADDR_POLICY_ACCEPT;
+ /* accept6/reject6 * produces an IPv6 wildcard address only.
+ * (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */
+ if ((fmt_flags & TAPMP_EXTENDED_STAR)
+ && (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) {
+ fmt_flags |= TAPMP_STAR_IPV6_ONLY;
+ }
+
if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
&newe.prt_min, &newe.prt_max) < 0) {
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
@@ -3785,9 +3842,12 @@ router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
return addr_policy_get_canonical_entry(&newe);
}
-/** Parse an exit policy line of the format "accept/reject private:...".
+/** Parse an exit policy line of the format "accept[6]/reject[6] private:...".
* This didn't exist until Tor 0.1.1.15, so nobody should generate it in
* router descriptors until earlier versions are obsolete.
+ *
+ * accept/reject and accept6/reject6 private all produce rules for both
+ * IPv4 and IPv6 addresses.
*/
static addr_policy_t *
router_parse_addr_policy_private(directory_token_t *tok)
@@ -3817,6 +3877,13 @@ router_parse_addr_policy_private(directory_token_t *tok)
result.prt_min = port_min;
result.prt_max = port_max;
+ if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) {
+ log_warn(LD_GENERAL,
+ "'%s' expands into rules which apply to all private IPv4 and "
+ "IPv6 addresses. (Use accept/reject private:* for IPv4 and "
+ "IPv6.)", tok->n_args == 1 ? tok->args[0] : "");
+ }
+
return addr_policy_get_canonical_entry(&result);
}
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index 85e4b7d88e..99fd52866c 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -41,7 +41,7 @@ extrainfo_t *extrainfo_parse_entry_from_string(const char *s, const char *end,
int cache_copy, struct digest_ri_map_t *routermap,
int *can_dl_again_out);
MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
- (const char *s, int assume_action));
+ (const char *s, int assume_action, int *malformed_list));
version_status_t tor_version_is_obsolete(const char *myversion,
const char *versionlist);
int tor_version_as_new_as(const char *platform, const char *cutoff);
diff --git a/src/or/routerset.c b/src/or/routerset.c
index 9fe5dffdeb..debe9ec6e1 100644
--- a/src/or/routerset.c
+++ b/src/or/routerset.c
@@ -85,10 +85,13 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
int added_countries = 0;
char *countryname;
smartlist_t *list = smartlist_new();
+ int malformed_list;
smartlist_split_string(list, s, ",",
SPLIT_SKIP_SPACE | SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(list, char *, nick) {
addr_policy_t *p;
+ /* if it doesn't pass our validation, assume it's malformed */
+ malformed_list = 1;
if (is_legal_hexdigest(nick)) {
char d[DIGEST_LEN];
if (*nick == '$')
@@ -104,17 +107,25 @@ routerset_parse(routerset_t *target, const char *s, const char *description)
description);
smartlist_add(target->country_names, countryname);
added_countries = 1;
- } else if ((strchr(nick,'.') || strchr(nick, '*')) &&
- (p = router_parse_addr_policy_item_from_string(
- nick, ADDR_POLICY_REJECT))) {
+ } else if ((strchr(nick,'.') || strchr(nick, ':') || strchr(nick, '*'))
+ && (p = router_parse_addr_policy_item_from_string(
+ nick, ADDR_POLICY_REJECT,
+ &malformed_list))) {
+ /* IPv4 addresses contain '.', IPv6 addresses contain ':',
+ * and wildcard addresses contain '*'. */
log_debug(LD_CONFIG, "Adding address %s to %s", nick, description);
smartlist_add(target->policies, p);
- } else {
- log_warn(LD_CONFIG, "Entry '%s' in %s is malformed.", nick,
- description);
+ } else if (malformed_list) {
+ log_warn(LD_CONFIG, "Entry '%s' in %s is malformed. Discarding entire"
+ " list.", nick, description);
r = -1;
tor_free(nick);
SMARTLIST_DEL_CURRENT(list, nick);
+ } else {
+ log_notice(LD_CONFIG, "Entry '%s' in %s is ignored. Using the"
+ " remainder of the list.", nick, description);
+ tor_free(nick);
+ SMARTLIST_DEL_CURRENT(list, nick);
}
} SMARTLIST_FOREACH_END(nick);
policy_expand_unspec(&target->policies);
diff --git a/src/or/statefile.c b/src/or/statefile.c
index dd1894beb7..7481cd71cb 100644
--- a/src/or/statefile.c
+++ b/src/or/statefile.c
@@ -372,6 +372,19 @@ or_state_load(void)
new_state = or_state_new();
} else if (contents) {
log_info(LD_GENERAL, "Loaded state from \"%s\"", fname);
+ /* Warn the user if their clock has been set backwards,
+ * they could be tricked into using old consensuses */
+ if (new_state->LastWritten > time(NULL)) {
+ char last_written_str[ISO_TIME_LEN+1];
+ char now_str[ISO_TIME_LEN+1];
+ format_iso_time(last_written_str, new_state->LastWritten),
+ format_iso_time(now_str, time(NULL));
+ log_warn(LD_GENERAL, "Your system clock has been set back in time. "
+ "Tor needs an accurate clock to know when the consensus "
+ "expires. You might have an empty clock battery or bad NTP "
+ "server. Clock time is %s, state file time is %s.",
+ now_str, last_written_str);
+ }
} else {
log_info(LD_GENERAL, "Initialized state");
}