diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/config.c | 44 | ||||
-rw-r--r-- | src/or/config.h | 4 | ||||
-rw-r--r-- | src/or/control.c | 43 | ||||
-rw-r--r-- | src/or/control.h | 3 | ||||
-rw-r--r-- | src/or/directory.c | 6 | ||||
-rw-r--r-- | src/or/directory.h | 4 | ||||
-rw-r--r-- | src/or/dirserv.c | 27 | ||||
-rw-r--r-- | src/or/dirvote.c | 8 | ||||
-rw-r--r-- | src/or/dirvote.h | 6 | ||||
-rw-r--r-- | src/or/geoip.c | 9 | ||||
-rw-r--r-- | src/or/geoip.h | 1 | ||||
-rw-r--r-- | src/or/include.am | 4 | ||||
-rw-r--r-- | src/or/keypin.c | 112 | ||||
-rw-r--r-- | src/or/keypin.h | 3 | ||||
-rw-r--r-- | src/or/main.c | 3 | ||||
-rw-r--r-- | src/or/or.h | 10 | ||||
-rw-r--r-- | src/or/policies.c | 212 | ||||
-rw-r--r-- | src/or/policies.h | 12 | ||||
-rw-r--r-- | src/or/rendcache.c | 136 | ||||
-rw-r--r-- | src/or/rendcache.h | 12 | ||||
-rw-r--r-- | src/or/rendcommon.c | 7 | ||||
-rw-r--r-- | src/or/rendservice.c | 150 | ||||
-rw-r--r-- | src/or/router.c | 6 | ||||
-rw-r--r-- | src/or/router.h | 2 | ||||
-rw-r--r-- | src/or/routerkeys.c | 92 | ||||
-rw-r--r-- | src/or/routerkeys.h | 2 | ||||
-rw-r--r-- | src/or/routerlist.c | 6 | ||||
-rw-r--r-- | src/or/routerparse.c | 83 | ||||
-rw-r--r-- | src/or/routerparse.h | 2 | ||||
-rw-r--r-- | src/or/routerset.c | 23 | ||||
-rw-r--r-- | src/or/statefile.c | 13 |
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"); } |