diff options
Diffstat (limited to 'src')
42 files changed, 789 insertions, 271 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index a10329c552..102d1bbc04 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -483,6 +483,13 @@ static const config_var_t option_vars_[] = { #ifdef _WIN32 V(GeoIPFile, FILENAME, "<default>"), V(GeoIPv6File, FILENAME, "<default>"), +#elif defined(__ANDROID__) + /* Android apps use paths that are configured at runtime. + * /data/local/tmp is guaranteed to exist, but will only be + * usable by the 'shell' and 'root' users, so this fallback is + * for debugging only. */ + V(GeoIPFile, FILENAME, "/data/local/tmp/geoip"), + V(GeoIPv6File, FILENAME, "/data/local/tmp/geoip6"), #else V(GeoIPFile, FILENAME, SHARE_DATADIR PATH_SEPARATOR "tor" PATH_SEPARATOR "geoip"), @@ -637,6 +644,7 @@ static const config_var_t option_vars_[] = { V(RephistTrackTime, INTERVAL, "24 hours"), V_IMMUTABLE(RunAsDaemon, BOOL, "0"), V(ReducedExitPolicy, BOOL, "0"), + V(ReevaluateExitPolicy, BOOL, "0"), OBSOLETE("RunTesting"), // currently unused V_IMMUTABLE(Sandbox, BOOL, "0"), V(SafeLogging, STRING, "1"), @@ -996,6 +1004,7 @@ set_options(or_options_t *new_val, char **msg) config_line_t *changes = config_get_changes(get_options_mgr(), old_options, new_val); control_event_conf_changed(changes); + connection_reapply_exit_policy(changes); config_free_lines(changes); } @@ -6898,6 +6907,15 @@ get_data_directory(const char *val) } else { return tor_strdup(get_windows_conf_root()); } +#elif defined(__ANDROID__) + /* Android apps can only use paths that are configured at runtime. + * /data/local/tmp is guaranteed to exist, but is only usable by the + * 'shell' and 'root' users, so this fallback is for debugging only. */ + if (val) { + return tor_strdup(val); + } else { + return tor_strdup("/data/local/tmp"); + } #else /* !defined(_WIN32) */ const char *d = val; if (!d) diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index 36b00662b5..624dc61bc5 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -141,6 +141,8 @@ struct or_options_t { * Includes OutboundBindAddresses and * configured ports. */ int ReducedExitPolicy; /**<Should we use the Reduced Exit Policy? */ + int ReevaluateExitPolicy; /**<Should we re-evaluate Exit Policy on existing + * connections when it changes? */ struct config_line_t *SocksPolicy; /**< Lists of socks policy components */ struct config_line_t *DirPolicy; /**< Lists of dir policy components */ /** Local address to bind outbound sockets */ diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c index b3f1c6d058..7f0d112c90 100644 --- a/src/app/main/shutdown.c +++ b/src/app/main/shutdown.c @@ -120,6 +120,7 @@ tor_free_all(int postfork) dirserv_free_all(); rep_hist_free_all(); bwhist_free_all(); + conflux_notify_shutdown(); circuit_free_all(); conflux_pool_free_all(); circpad_machines_free(); diff --git a/src/config/torrc.sample.in b/src/config/torrc.sample.in index 639d7c4d68..97ff941459 100644 --- a/src/config/torrc.sample.in +++ b/src/config/torrc.sample.in @@ -225,6 +225,10 @@ #ExitPolicy accept6 *6:119 # accept nntp ports on IPv6 only as well as default exit policy #ExitPolicy reject *:* # no exits allowed +## Uncomment this if you want your exit relay to reevaluate its exit policy on +## existing connections when the exit policy is modified. +#ReevaluateExitPolicy 1 + ## Bridge relays (or "bridges") are Tor relays that aren't listed in the ## main directory. Since there is no complete public list of them, even an ## ISP that filters connections to all the known Tor relays probably diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index b90c7ebb58..643d97b064 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -65,6 +65,7 @@ #include "core/or/conflux.h" #include "core/or/conflux_pool.h" #include "core/or/crypt_path.h" +#include "core/or/dos.h" #include "core/or/extendinfo.h" #include "core/or/status.h" #include "core/or/trace_probes_circuit.h" @@ -1130,6 +1131,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) cell_queue_init(&circ->p_chan_cells); init_circuit_base(TO_CIRCUIT(circ)); + dos_stream_init_circ_tbf(circ); tor_trace(TR_SUBSYS(circuit), TR_EV(new_or), circ); return circ; diff --git a/src/core/or/conflux_pool.c b/src/core/or/conflux_pool.c index 3a8f6ec8e3..34417f8119 100644 --- a/src/core/or/conflux_pool.c +++ b/src/core/or/conflux_pool.c @@ -1607,7 +1607,22 @@ linked_circuit_free(circuit_t *circ, bool is_client) /* Circuit can be freed without being closed and so we try to delete this leg * so we can learn if this circuit is the last leg or not. */ - cfx_del_leg(circ->conflux, circ); + if (cfx_del_leg(circ->conflux, circ)) { + /* Check for instances of bug #40870, which we suspect happen + * during exit. If any happen outside of exit, BUG and warn. */ + if (!circ->conflux->in_full_teardown) { + /* We should bug and warn if we're not in a shutdown process; that + * means we got here somehow without a close. */ + if (BUG(!shutting_down)) { + log_warn(LD_BUG, + "Conflux circuit %p being freed without being marked for " + "full teardown via close, with shutdown state %d. " + "Please report this.", circ, shutting_down); + conflux_log_set(LOG_WARN, circ->conflux, is_client); + } + circ->conflux->in_full_teardown = true; + } + } if (CONFLUX_NUM_LEGS(circ->conflux) > 0) { /* The last leg will free the streams but until then, we nullify to avoid @@ -2129,14 +2144,36 @@ conflux_log_set(int loglevel, const conflux_t *cfx, bool is_client) } } +/** + * Conflux needs a notification when tor_shutdown() begins, so that + * when circuits are freed, new legs are not launched. + * + * This needs a separate notification from conflux_pool_free_all(), + * because circuits must be freed before that function. + */ +void +conflux_notify_shutdown(void) +{ + shutting_down = true; +} + +#ifdef TOR_UNIT_TESTS +/** + * For unit tests: Clear the shutting down state so we resume building legs. + */ +void +conflux_clear_shutdown(void) +{ + shutting_down = false; +} +#endif + /** Free and clean up the conflux pool subsystem. This is called by the subsys * manager AFTER all circuits have been freed which implies that all objects in * the pools aren't referenced anymore. */ void conflux_pool_free_all(void) { - shutting_down = true; - digest256map_free(client_linked_pool, free_conflux_void_); digest256map_free(server_linked_pool, free_conflux_void_); digest256map_free(client_unlinked_pool, free_unlinked_void_); diff --git a/src/core/or/conflux_pool.h b/src/core/or/conflux_pool.h index afa4d9d058..eba726b03a 100644 --- a/src/core/or/conflux_pool.h +++ b/src/core/or/conflux_pool.h @@ -12,6 +12,7 @@ #include "core/or/or.h" void conflux_pool_init(void); +void conflux_notify_shutdown(void); void conflux_pool_free_all(void); origin_circuit_t *conflux_get_circ_for_conn(const entry_connection_t *conn, @@ -41,6 +42,7 @@ void conflux_log_set(int loglevel, const conflux_t *cfx, bool is_client); #ifdef TOR_UNIT_TESTS bool launch_new_set(int num_legs); +void conflux_clear_shutdown(void); digest256map_t *get_linked_pool(bool is_client); digest256map_t *get_unlinked_pool(bool is_client); extern uint8_t DEFAULT_CLIENT_UX; diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index f21779a80c..b36d0d9013 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -73,6 +73,7 @@ #include "core/or/conflux_util.h" #include "core/or/circuitstats.h" #include "core/or/connection_or.h" +#include "core/or/dos.h" #include "core/or/extendinfo.h" #include "core/or/policies.h" #include "core/or/reasons.h" @@ -105,6 +106,7 @@ #include "lib/buf/buffers.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "lib/encoding/confline.h" #include "core/or/cell_st.h" #include "core/or/cpath_build_state_st.h" @@ -3989,6 +3991,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) begin_cell_t bcell; int rv; uint8_t end_reason=0; + dos_stream_defense_type_t dos_defense_type; assert_circuit_ok(circ); if (!CIRCUIT_IS_ORIGIN(circ)) { @@ -4147,6 +4150,24 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) log_debug(LD_EXIT,"about to start the dns_resolve()."); + // in the future we may want to have a similar defense for BEGIN_DIR and + // BEGIN sent to OS. + dos_defense_type = dos_stream_new_begin_or_resolve_cell(or_circ); + switch (dos_defense_type) { + case DOS_STREAM_DEFENSE_NONE: + break; + case DOS_STREAM_DEFENSE_REFUSE_STREAM: + // we don't use END_STREAM_REASON_RESOURCELIMIT because it would make a + // client mark us as non-functional until they get a new consensus. + relay_send_end_cell_from_edge(rh.stream_id, circ, END_STREAM_REASON_MISC, + layer_hint); + connection_free_(TO_CONN(n_stream)); + return 0; + case DOS_STREAM_DEFENSE_CLOSE_CIRCUIT: + connection_free_(TO_CONN(n_stream)); + return -END_CIRC_REASON_RESOURCELIMIT; + } + /* send it off to the gethostbyname farm */ switch (dns_resolve(n_stream)) { case 1: /* resolve worked; now n_stream is attached to circ. */ @@ -4170,17 +4191,21 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) * Called when we receive a RELAY_COMMAND_RESOLVE cell 'cell' along the * circuit <b>circ</b>; * begin resolving the hostname, and (eventually) reply with a RESOLVED cell. + * + * Return -(some circuit end reason) if we want to tear down <b>circ</b>. + * Else return 0. */ int connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) { edge_connection_t *dummy_conn; relay_header_t rh; + dos_stream_defense_type_t dos_defense_type; assert_circuit_ok(TO_CIRCUIT(circ)); relay_header_unpack(&rh, cell->payload); if (rh.length > RELAY_PAYLOAD_SIZE) - return -1; + return 0; /* Note the RESOLVE stream as seen. */ rep_hist_note_exit_stream(RELAY_COMMAND_RESOLVE); @@ -4203,6 +4228,19 @@ connection_exit_begin_resolve(cell_t *cell, or_circuit_t *circ) dummy_conn->on_circuit = TO_CIRCUIT(circ); + dos_defense_type = dos_stream_new_begin_or_resolve_cell(circ); + switch (dos_defense_type) { + case DOS_STREAM_DEFENSE_NONE: + break; + case DOS_STREAM_DEFENSE_REFUSE_STREAM: + dns_send_resolved_error_cell(dummy_conn, RESOLVED_TYPE_ERROR_TRANSIENT); + connection_free_(TO_CONN(dummy_conn)); + return 0; + case DOS_STREAM_DEFENSE_CLOSE_CIRCUIT: + connection_free_(TO_CONN(dummy_conn)); + return -END_CIRC_REASON_RESOURCELIMIT; + } + /* send it off to the gethostbyname farm */ switch (dns_resolve(dummy_conn)) { case -1: /* Impossible to resolve; a resolved cell was sent. */ @@ -4237,6 +4275,76 @@ my_exit_policy_rejects(const tor_addr_t *addr, return 0; } +/* Reapply exit policy to existing connections, possibly terminating + * connections + * no longer allowed by the policy. + */ +void +connection_reapply_exit_policy(config_line_t *changes) +{ + int marked_for_close = 0; + smartlist_t *conn_list = NULL; + smartlist_t *policy = NULL; + int config_change_relevant = 0; + + if (get_options()->ReevaluateExitPolicy == 0) { + return; + } + + for (const config_line_t *line = changes; + line && !config_change_relevant; + line = line->next) { + const char* exit_policy_options[] = { + "ExitRelay", + "ExitPolicy", + "ReducedExitPolicy", + "ReevaluateExitPolicy", + "IPv6Exit", + NULL + }; + for (unsigned int i = 0; exit_policy_options[i] != NULL; ++i) { + if (strcmp(line->key, exit_policy_options[i]) == 0) { + config_change_relevant = 1; + break; + } + } + } + + if (!config_change_relevant) { + /* Policy did not change: no need to iterate over connections */ + return; + } + + // we can't use router_compare_to_my_exit_policy as it depend on the + // descriptor, which is regenerated asynchronously, so we have to parse the + // policy ourselves. + // We don't verify for our own IP, it's not part of the configuration. + if (BUG(policies_parse_exit_policy_from_options(get_options(), NULL, NULL, + &policy) != 0)) { + return; + } + + conn_list = connection_list_by_type_purpose(CONN_TYPE_EXIT, + EXIT_PURPOSE_CONNECT); + + SMARTLIST_FOREACH_BEGIN(conn_list, connection_t *, conn) { + addr_policy_result_t verdict = compare_tor_addr_to_addr_policy(&conn->addr, + conn->port, + policy); + if (verdict != ADDR_POLICY_ACCEPTED) { + connection_edge_end(TO_EDGE_CONN(conn), END_STREAM_REASON_EXITPOLICY); + connection_mark_for_close(conn); + ++marked_for_close; + } + } SMARTLIST_FOREACH_END(conn); + + smartlist_free(conn_list); + smartlist_free(policy); + + log_info(LD_GENERAL, "Marked %d connections to be closed as no longer " + "allowed per ExitPolicy", marked_for_close); +} + /** Return true iff the consensus allows network reentry. The default value is * false if the parameter is not found. */ static bool diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 59fc17dea5..1bb0e6d368 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -13,6 +13,7 @@ #define TOR_CONNECTION_EDGE_H #include "lib/testsupport/testsupport.h" +#include "lib/encoding/confline.h" #include "feature/hs/hs_service.h" @@ -101,6 +102,7 @@ void connection_entry_set_controller_wait(entry_connection_t *conn); void connection_ap_about_to_close(entry_connection_t *edge_conn); void connection_exit_about_to_close(edge_connection_t *edge_conn); +void connection_reapply_exit_policy(config_line_t *changes); MOCK_DECL(int, connection_ap_handshake_send_begin,(entry_connection_t *ap_conn)); diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 343c1a67ed..30ce5e0c57 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -104,7 +104,7 @@ static void connection_or_check_canonicity(or_connection_t *conn, /** * Cast a `connection_t *` to an `or_connection_t *`. * - * Exit with an assertion failure if the input is not an `or_connnection_t`. + * Exit with an assertion failure if the input is not an `or_connection_t`. **/ or_connection_t * TO_OR_CONN(connection_t *c) @@ -116,7 +116,7 @@ TO_OR_CONN(connection_t *c) /** * Cast a `const connection_t *` to a `const or_connection_t *`. * - * Exit with an assertion failure if the input is not an `or_connnection_t`. + * Exit with an assertion failure if the input is not an `or_connection_t`. **/ const or_connection_t * CONST_TO_OR_CONN(const connection_t *c) diff --git a/src/core/or/dos.c b/src/core/or/dos.c index ccdb30dbee..b789f87aae 100644 --- a/src/core/or/dos.c +++ b/src/core/or/dos.c @@ -79,6 +79,24 @@ static uint64_t conn_num_addr_connect_rejected; static uint32_t dos_num_circ_max_outq; /* + * Stream denial of service mitigation. + * + * Namespace used for this mitigation framework is "dos_stream_". + */ + +/* Is the connection DoS mitigation enabled? */ +static unsigned int dos_stream_enabled = 0; + +/* Consensus parameters. They can be changed when a new consensus arrives. + * They are initialized with the hardcoded default values. */ +static dos_stream_defense_type_t dos_stream_defense_type; +static uint32_t dos_stream_rate = DOS_STREAM_RATE_DEFAULT; +static uint32_t dos_stream_burst = DOS_STREAM_BURST_DEFAULT; + +/* Keep some stats for the heartbeat so we can report out. */ +static uint64_t stream_num_rejected; + +/* * General interface of the denial of service mitigation subsystem. */ @@ -258,6 +276,59 @@ get_param_conn_connect_defense_time_period(const networkstatus_t *ns) INT32_MAX); } +/* Return true iff the stream creation mitigation is enabled. We look at the + * consensus for this else a default value is returned. */ +MOCK_IMPL(STATIC unsigned int, +get_param_stream_enabled, (const networkstatus_t *ns)) +{ + if (dos_get_options()->DoSStreamCreationEnabled != -1) { + return dos_get_options()->DoSStreamCreationEnabled; + } + + return !!networkstatus_get_param(ns, "DoSStreamCreationEnabled", + DOS_STREAM_ENABLED_DEFAULT, 0, 1); +} + +/* Return the parameter for the time rate that is how many stream per circuit + * over this time span. */ +static uint32_t +get_param_stream_rate(const networkstatus_t *ns) +{ + /* This is in seconds. */ + if (dos_get_options()->DoSStreamCreationRate) { + return dos_get_options()->DoSStreamCreationRate; + } + return networkstatus_get_param(ns, "DoSStreamCreationRate", + DOS_STREAM_RATE_DEFAULT, + 1, INT32_MAX); +} + +/* Return the parameter for the maximum circuit count for the circuit time + * rate. */ +static uint32_t +get_param_stream_burst(const networkstatus_t *ns) +{ + if (dos_get_options()->DoSStreamCreationBurst) { + return dos_get_options()->DoSStreamCreationBurst; + } + return networkstatus_get_param(ns, "DoSStreamCreationBurst", + DOS_STREAM_BURST_DEFAULT, + 1, INT32_MAX); +} + +/* Return the consensus parameter of the circuit creation defense type. */ +static uint32_t +get_param_stream_defense_type(const networkstatus_t *ns) +{ + if (dos_get_options()->DoSStreamCreationDefenseType) { + return dos_get_options()->DoSStreamCreationDefenseType; + } + return networkstatus_get_param(ns, "DoSStreamCreationDefenseType", + DOS_STREAM_DEFENSE_TYPE_DEFAULT, + DOS_STREAM_DEFENSE_NONE, + DOS_STREAM_DEFENSE_MAX); +} + /* Set circuit creation parameters located in the consensus or their default * if none are present. Called at initialization or when the consensus * changes. */ @@ -283,6 +354,12 @@ set_dos_parameters(const networkstatus_t *ns) /* Circuit. */ dos_num_circ_max_outq = get_param_dos_num_circ_max_outq(ns); + + /* Stream. */ + dos_stream_enabled = get_param_stream_enabled(ns); + dos_stream_defense_type = get_param_stream_defense_type(ns); + dos_stream_rate = get_param_stream_rate(ns); + dos_stream_burst = get_param_stream_burst(ns); } /* Free everything for the circuit creation DoS mitigation subsystem. */ @@ -760,6 +837,48 @@ dos_conn_addr_get_defense_type(const tor_addr_t *addr) return DOS_CONN_DEFENSE_NONE; } +/* Stream creation public API. */ + +/** Return the number of rejected stream and resolve. */ +uint64_t +dos_get_num_stream_rejected(void) +{ + return stream_num_rejected; +} + +/* Return the action to take against a BEGIN or RESOLVE cell. Return + * DOS_STREAM_DEFENSE_NONE when no action should be taken. + * Increment the appropriate counter when the cell was found to go over a + * limit. */ +dos_stream_defense_type_t +dos_stream_new_begin_or_resolve_cell(or_circuit_t *circ) +{ + if (!dos_stream_enabled || circ == NULL) + return DOS_STREAM_DEFENSE_NONE; + + token_bucket_ctr_refill(&circ->stream_limiter, + (uint32_t) monotime_coarse_absolute_sec()); + + if (token_bucket_ctr_get(&circ->stream_limiter) > 0) { + token_bucket_ctr_dec(&circ->stream_limiter, 1); + return DOS_STREAM_DEFENSE_NONE; + } + /* if defense type is DOS_STREAM_DEFENSE_NONE but DoSStreamEnabled is true, + * we count offending cells as rejected, despite them being actually + * accepted. */ + ++stream_num_rejected; + return dos_stream_defense_type; +} + +/* Initialize the token bucket for stream rate limit on a circuit. */ +void +dos_stream_init_circ_tbf(or_circuit_t *circ) +{ + token_bucket_ctr_init(&circ->stream_limiter, dos_stream_rate, + dos_stream_burst, + (uint32_t) monotime_coarse_absolute_sec()); +} + /* General API */ /* Take any appropriate actions for the given geoip entry that is about to get @@ -945,6 +1064,14 @@ dos_log_heartbeat(void) "[DoSRefuseSingleHopClientRendezvous disabled]"); } + if (dos_stream_enabled) { + smartlist_add_asprintf(elems, + "%" PRIu64 " stream rejected", + stream_num_rejected); + } else { + smartlist_add_asprintf(elems, "[DoSStreamCreationEnabled disabled]"); + } + /* HS DoS stats. */ smartlist_add_asprintf(elems, "%" PRIu64 " INTRODUCE2 rejected", diff --git a/src/core/or/dos.h b/src/core/or/dos.h index 4a2227f132..03606287d1 100644 --- a/src/core/or/dos.h +++ b/src/core/or/dos.h @@ -90,6 +90,7 @@ uint64_t dos_get_num_cc_rejected(void); uint64_t dos_get_num_conn_addr_rejected(void); uint64_t dos_get_num_conn_addr_connect_rejected(void); uint64_t dos_get_num_single_hop_refused(void); +uint64_t dos_get_num_stream_rejected(void); /* * Circuit creation DoS mitigation subsystemn interface. @@ -159,6 +160,37 @@ typedef enum dos_conn_defense_type_t { dos_conn_defense_type_t dos_conn_addr_get_defense_type(const tor_addr_t *addr); +/* + * Stream creation DoS mitigation subsystem interface. + */ + +/* DoSStreamCreationEnabled default. Disabled by deault. */ +#define DOS_STREAM_ENABLED_DEFAULT 0 +/* DoSStreamCreationDefenseType maps to the dos_stream_defense_type_t enum */ +#define DOS_STREAM_DEFENSE_TYPE_DEFAULT DOS_STREAM_DEFENSE_REFUSE_STREAM +/* DosStreamCreationRate is 100 per seconds. */ +#define DOS_STREAM_RATE_DEFAULT 100 +/* DosStreamCreationBurst default. */ +#define DOS_STREAM_BURST_DEFAULT 300 + +/* Type of defense that we can use for the stream creation DoS mitigation. */ +typedef enum dos_stream_defense_type_t { + /* No defense used. */ + DOS_STREAM_DEFENSE_NONE = 1, + /* Reject the stream */ + DOS_STREAM_DEFENSE_REFUSE_STREAM = 2, + /* Close the circuit */ + DOS_STREAM_DEFENSE_CLOSE_CIRCUIT = 3, + + /* Maximum value that can be used. Useful for the boundaries of the + * consensus parameter. */ + DOS_STREAM_DEFENSE_MAX = 3, +} dos_stream_defense_type_t; + +dos_stream_defense_type_t dos_stream_new_begin_or_resolve_cell( + or_circuit_t *circ); +void dos_stream_init_circ_tbf(or_circuit_t *circ); + #ifdef DOS_PRIVATE STATIC uint32_t get_param_conn_max_concurrent_count( @@ -176,6 +208,8 @@ MOCK_DECL(STATIC unsigned int, get_param_cc_enabled, (const networkstatus_t *ns)); MOCK_DECL(STATIC unsigned int, get_param_conn_enabled, (const networkstatus_t *ns)); +MOCK_DECL(STATIC unsigned int, get_param_stream_enabled, + (const networkstatus_t *ns)); #endif /* defined(DOS_PRIVATE) */ diff --git a/src/core/or/dos_options.inc b/src/core/or/dos_options.inc index 9baa7a35b8..4d15c33f3d 100644 --- a/src/core/or/dos_options.inc +++ b/src/core/or/dos_options.inc @@ -50,6 +50,19 @@ CONF_VAR(DoSConnectionConnectBurst, POSINT, 0, "0") /** Allowed rate of client connection allowed per address. */ CONF_VAR(DoSConnectionConnectRate, POSINT, 0, "0") +/** Autobool: Is the stream creation DoS mitigation subsystem enabled? */ +CONF_VAR(DoSStreamCreationEnabled, AUTOBOOL, 0, "auto") + +/** Stream rate used to refill the token bucket. */ +CONF_VAR(DoSStreamCreationRate, POSINT, 0, "0") + +/** Maximum allowed burst of stream. */ +CONF_VAR(DoSStreamCreationBurst, POSINT, 0, "0") + +/** When an circuit is detected as malicious, what defense should be used + * against it. See the dos_stream_defense_type_t enum. */ +CONF_VAR(DoSStreamCreationDefenseType, INT, 0, "0") + /** For how much time (in seconds) the connection connect rate defense is * applicable for a malicious address. A random time delta is added to the * defense time of an address which will be between 1 second and half of this diff --git a/src/core/or/or_circuit_st.h b/src/core/or/or_circuit_st.h index d5a7007928..28e357338a 100644 --- a/src/core/or/or_circuit_st.h +++ b/src/core/or/or_circuit_st.h @@ -102,6 +102,10 @@ struct or_circuit_t { * used if this is a service introduction circuit at the intro point * (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */ token_bucket_ctr_t introduce2_bucket; + + /** RELAY_BEGIN and RELAY_RESOLVE cell bucket controlling how much can go on + * this circuit. Only used if this is the end of a circuit on an exit node.*/ + token_bucket_ctr_t stream_limiter; }; #endif /* !defined(OR_CIRCUIT_ST_H) */ diff --git a/src/core/or/policies.c b/src/core/or/policies.c index 1864b84d5e..4641632b60 100644 --- a/src/core/or/policies.c +++ b/src/core/or/policies.c @@ -1066,7 +1066,7 @@ socks_policy_permits_address(const tor_addr_t *addr) } /** Return 1 if <b>addr</b> is permitted to connect to our metrics port, - * based on <b>socks_policy</b>. Else return 0. + * based on <b>metrics_policy</b>. Else return 0. */ int metrics_policy_permits_address(const tor_addr_t *addr) diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 6abe802355..0cda6e7bf7 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -2015,8 +2015,7 @@ handle_relay_cell_command(cell_t *cell, circuit_t *circ, circ->purpose); return 0; } - connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ)); - return 0; + return connection_exit_begin_resolve(cell, TO_OR_CIRCUIT(circ)); case RELAY_COMMAND_RESOLVED: if (conn) { log_fn(LOG_PROTOCOL_WARN, domain, diff --git a/src/core/or/scheduler_kist.c b/src/core/or/scheduler_kist.c index 69804247c8..c4b15a9950 100644 --- a/src/core/or/scheduler_kist.c +++ b/src/core/or/scheduler_kist.c @@ -447,10 +447,16 @@ update_socket_written(socket_table_t *table, channel_t *chan, size_t bytes) * by only writing a channel's outbuf to the kernel if it has 8 cells or more * in it. * - * Note: The number 8 has been picked for no particular reasons except that it - * is 4096 bytes which is a common number for buffering. A TLS record can hold - * up to 16KiB thus using 8 cells means that a relay will at most send a TLS - * record of 4KiB or 1/4 of the maximum capacity of a TLS record. + * Note: The number 8 was picked so that, when using 512-byte cells, it + * would produce 4096 bytes: a common number for buffering. A TLS + * record can hold up to 16KiB; thus, using 8 512-byte cells means that + * a relay will at most send a TLS record of 4KiB or 1/4 of the maximum + * capacity of a TLS record. + * + * Of course, the above calculation became incorrect when we moved to + * 514-byte cells in order to accommodate a 4-byte circuit ID; we may + * want to consider profiling with '7' to see if it produces better + * results. (TODO) */ MOCK_IMPL(int, channel_should_write_to_kernel, (outbuf_table_t *table, channel_t *chan)) diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index f98513ef75..862ea9dafe 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -16,9 +16,12 @@ #include "lib/encoding/confline.h" #include "lib/confmgt/confmgt.h" #include "lib/conf/confdecl.h" +#include "lib/version/torversion.h" /* Required for dirinfo_type_t in or_options_t */ #include "core/or/or.h" +#include "core/or/tor_version_st.h" +#include "core/or/versions.h" #include "app/config/config.h" #include "app/config/resolve_addr.h" @@ -426,6 +429,7 @@ static int dirauth_options_validate(const void *arg, char **msg) { const dirauth_options_t *options = arg; + tor_version_t minimal_accepted_server_version, recommended_version; if (options->VersioningAuthoritativeDirectory && (!options->RecommendedClientVersions || @@ -439,12 +443,53 @@ dirauth_options_validate(const void *arg, char **msg) REJECT("Guard bandwdith threshold fraction is invalid."); } - char *t; + if (tor_version_parse(options->MinimalAcceptedServerVersion, + &minimal_accepted_server_version) != 0) { + REJECT("Invalid MinimalAcceptedServerVersion"); + } + + tor_assertf(tor_version_parse(get_short_version(), + &recommended_version) == 0, + "We failed to parse our own version"); + if (tor_version_compare(&recommended_version, + &minimal_accepted_server_version) < 0) { + REJECT("MinimalAcceptedServerVersion wants to reject the version " + "this node is running"); + } + + char *recommended_versions; + int found_recommended_rejected_version = 0; /* Call these functions to produce warnings only. */ - t = format_recommended_version_list(options->RecommendedClientVersions, 1); - tor_free(t); - t = format_recommended_version_list(options->RecommendedServerVersions, 1); - tor_free(t); + recommended_versions = format_recommended_version_list( + options->RecommendedClientVersions, 1); + tor_free(recommended_versions); + + recommended_versions = format_recommended_version_list( + options->RecommendedServerVersions, 1); + + smartlist_t *version_sl = smartlist_new(); + smartlist_split_string(version_sl, recommended_versions, ",", + SPLIT_SKIP_SPACE, 0); + SMARTLIST_FOREACH_BEGIN(version_sl, const char *, version) { + if (tor_version_parse(version, + &recommended_version) != 0) { + COMPLAIN("Found unparseable version in RecommendedServerVersions"); + continue; + } + + if (tor_version_compare(&recommended_version, + &minimal_accepted_server_version) < 0) { + found_recommended_rejected_version = 1; + break; + } + } SMARTLIST_FOREACH_END(version); + + SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version)); + smartlist_free(version_sl); + tor_free(recommended_versions); + if (found_recommended_rejected_version) + REJECT("MinimalAcceptedServerVersion wants to reject a recommended " + "version"); if (options->TestingAuthDirTimeToLearnReachability > 2*60*60) { COMPLAIN("TestingAuthDirTimeToLearnReachability is insanely high."); diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc index e2056c9cc7..9284c31a59 100644 --- a/src/feature/dirauth/dirauth_options.inc +++ b/src/feature/dirauth/dirauth_options.inc @@ -76,6 +76,9 @@ CONF_VAR(RecommendedClientVersions, LINELIST, 0, NULL) /** Which versions of tor should we tell users to run on relays? */ CONF_VAR(RecommendedServerVersions, LINELIST, 0, NULL) +/** Which minimal version of tor do we accept relay descriptors from? */ +CONF_VAR(MinimalAcceptedServerVersion, STRING, 0, "0.4.7.0-alpha-dev") + /** Relays which should be voted Guard regardless of uptime and bandwidth. */ CONF_VAR(AuthDirVoteGuard, ROUTERSET, 0, NULL) diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 1080415827..f8290cde6d 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -1631,7 +1631,11 @@ networkstatus_compute_consensus(smartlist_t *votes, n_versioning_servers); client_versions = compute_consensus_versions_list(combined_client_versions, n_versioning_clients); - packages = compute_consensus_package_lines(votes); + + if (consensus_method < MIN_METHOD_TO_OMIT_PACKAGE_FINGERPRINTS) + packages = tor_strdup(""); + else + packages = compute_consensus_package_lines(votes); SMARTLIST_FOREACH(combined_server_versions, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(combined_client_versions, char *, cp, tor_free(cp)); @@ -1776,15 +1780,10 @@ networkstatus_compute_consensus(smartlist_t *votes, } { - if (consensus_method < MIN_METHOD_FOR_CORRECT_BWWEIGHTSCALE) { - max_unmeasured_bw_kb = (int32_t) extract_param_buggy( - params, "maxunmeasuredbw", DEFAULT_MAX_UNMEASURED_BW_KB); - } else { - max_unmeasured_bw_kb = dirvote_get_intermediate_param_value( - param_list, "maxunmeasuredbw", DEFAULT_MAX_UNMEASURED_BW_KB); - if (max_unmeasured_bw_kb < 1) - max_unmeasured_bw_kb = 1; - } + max_unmeasured_bw_kb = dirvote_get_intermediate_param_value( + param_list, "maxunmeasuredbw", DEFAULT_MAX_UNMEASURED_BW_KB); + if (max_unmeasured_bw_kb < 1) + max_unmeasured_bw_kb = 1; } /* Add the actual router entries. */ @@ -2130,7 +2129,7 @@ networkstatus_compute_consensus(smartlist_t *votes, /* Starting with consensus method 32, we handle the middle-only * flag specially: when it is present, we clear some flags, and * set others. */ - if (is_middle_only && consensus_method >= MIN_METHOD_FOR_MIDDLEONLY) { + if (is_middle_only) { remove_flag(chosen_flags, "Exit"); remove_flag(chosen_flags, "V2Dir"); remove_flag(chosen_flags, "Guard"); @@ -2367,15 +2366,10 @@ networkstatus_compute_consensus(smartlist_t *votes, { int64_t weight_scale; - if (consensus_method < MIN_METHOD_FOR_CORRECT_BWWEIGHTSCALE) { - weight_scale = extract_param_buggy(params, "bwweightscale", - BW_WEIGHT_SCALE); - } else { - weight_scale = dirvote_get_intermediate_param_value( - param_list, "bwweightscale", BW_WEIGHT_SCALE); - if (weight_scale < 1) - weight_scale = 1; - } + weight_scale = dirvote_get_intermediate_param_value( + param_list, "bwweightscale", BW_WEIGHT_SCALE); + if (weight_scale < 1) + weight_scale = 1; added_weights = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, weight_scale); } @@ -2477,53 +2471,6 @@ networkstatus_compute_consensus(smartlist_t *votes, return result; } -/** Extract the value of a parameter from a string encoding a list of - * parameters, badly. - * - * This is a deliberately buggy implementation, for backward compatibility - * with versions of Tor affected by #19011. Once all authorities have - * upgraded to consensus method 31 or later, then we can throw away this - * function. */ -STATIC int64_t -extract_param_buggy(const char *params, - const char *param_name, - int64_t default_value) -{ - int64_t value = default_value; - const char *param_str = NULL; - - if (params) { - char *prefix1 = NULL, *prefix2=NULL; - tor_asprintf(&prefix1, "%s=", param_name); - tor_asprintf(&prefix2, " %s=", param_name); - if (strcmpstart(params, prefix1) == 0) - param_str = params; - else - param_str = strstr(params, prefix2); - tor_free(prefix1); - tor_free(prefix2); - } - - if (param_str) { - int ok=0; - char *eq = strchr(param_str, '='); - if (eq) { - value = tor_parse_long(eq+1, 10, 1, INT32_MAX, &ok, NULL); - if (!ok) { - log_warn(LD_DIR, "Bad element '%s' in %s", - escaped(param_str), param_name); - value = default_value; - } - } else { - log_warn(LD_DIR, "Bad element '%s' in %s", - escaped(param_str), param_name); - value = default_value; - } - } - - return value; -} - /** Given a list of networkstatus_t for each vote, return a newly allocated * string containing the "package" lines for the vote. */ STATIC char * @@ -3917,6 +3864,7 @@ dirvote_get_vote(const char *fp, int flags) STATIC microdesc_t * dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) { + (void) consensus_method; // Currently unneeded... microdesc_t *result = NULL; char *key = NULL, *summary = NULL, *family = NULL; size_t keylen; @@ -3935,20 +3883,15 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method) if (ri->onion_curve25519_pkey) { char kbuf[CURVE25519_BASE64_PADDED_LEN + 1]; - bool add_padding = (consensus_method < MIN_METHOD_FOR_UNPADDED_NTOR_KEY); - curve25519_public_to_base64(kbuf, ri->onion_curve25519_pkey, add_padding); + curve25519_public_to_base64(kbuf, ri->onion_curve25519_pkey, false); smartlist_add_asprintf(chunks, "ntor-onion-key %s\n", kbuf); } if (family) { - if (consensus_method < MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS) { - smartlist_add_asprintf(chunks, "family %s\n", family); - } else { - const uint8_t *id = (const uint8_t *)ri->cache_info.identity_digest; - char *canonical_family = nodefamily_canonicalize(family, id, 0); - smartlist_add_asprintf(chunks, "family %s\n", canonical_family); - tor_free(canonical_family); - } + const uint8_t *id = (const uint8_t *)ri->cache_info.identity_digest; + char *canonical_family = nodefamily_canonicalize(family, id, 0); + smartlist_add_asprintf(chunks, "family %s\n", canonical_family); + tor_free(canonical_family); } if (summary && strcmp(summary, "reject 1-65535")) @@ -4046,10 +3989,6 @@ static const struct consensus_method_range_t { int high; } microdesc_consensus_methods[] = { {MIN_SUPPORTED_CONSENSUS_METHOD, - MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS - 1}, - {MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS, - MIN_METHOD_FOR_UNPADDED_NTOR_KEY - 1}, - {MIN_METHOD_FOR_UNPADDED_NTOR_KEY, MAX_SUPPORTED_CONSENSUS_METHOD}, {-1, -1} }; diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index ae8d43a6f0..6ac07f171a 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -50,29 +50,10 @@ ((MIN_VOTE_SECONDS_TESTING)+(MIN_DIST_SECONDS_TESTING)+1) /** The lowest consensus method that we currently support. */ -#define MIN_SUPPORTED_CONSENSUS_METHOD 28 +#define MIN_SUPPORTED_CONSENSUS_METHOD 32 /** The highest consensus method that we currently support. */ -#define MAX_SUPPORTED_CONSENSUS_METHOD 33 - -/** - * Lowest consensus method where microdescriptor lines are put in canonical - * form for improved compressibility and ease of storage. See proposal 298. - **/ -#define MIN_METHOD_FOR_CANONICAL_FAMILIES_IN_MICRODESCS 29 - -/** Lowest consensus method where an unpadded base64 onion-key-ntor is allowed - * See #7869 */ -#define MIN_METHOD_FOR_UNPADDED_NTOR_KEY 30 - -/** Lowest consensus method for which we use the correct algorithm for - * extracting the bwweightscale= and maxunmeasuredbw= parameters. See #19011. - */ -#define MIN_METHOD_FOR_CORRECT_BWWEIGHTSCALE 31 - -/** Lowest consensus method for which we handle the MiddleOnly flag specially. - */ -#define MIN_METHOD_FOR_MIDDLEONLY 32 +#define MAX_SUPPORTED_CONSENSUS_METHOD 34 /** * Lowest consensus method for which we suppress the published time in @@ -80,6 +61,12 @@ */ #define MIN_METHOD_TO_SUPPRESS_MD_PUBLISHED 33 +/** + * Lowest (supported) consensus method for which we do not include + * any "package" lines. + **/ +#define MIN_METHOD_TO_OMIT_PACKAGE_FINGERPRINTS 34 + /** Default bandwidth to clip unmeasured bandwidths to using method >= * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * get confused with the above macros.) */ @@ -274,9 +261,6 @@ STATIC char *networkstatus_get_detached_signatures(smartlist_t *consensuses); STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method); -STATIC int64_t extract_param_buggy(const char *params, - const char *param_name, - int64_t default_value); #endif /* defined(DIRVOTE_PRIVATE) */ diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 7fd930e246..95acb31173 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -404,8 +404,8 @@ dirserv_rejects_tor_version(const char *platform, static const char please_upgrade_string[] = "Tor version is insecure or unsupported. Please upgrade!"; - /* Anything before 0.4.7.0 is unsupported. Reject them. */ - if (!tor_version_as_new_as(platform,"0.4.7.0-alpha-dev")) { + if (!tor_version_as_new_as(platform, + dirauth_get_options()->MinimalAcceptedServerVersion)) { if (msg) { *msg = please_upgrade_string; } diff --git a/src/feature/hs/hs_service.h b/src/feature/hs/hs_service.h index 36d67719ca..b3a4117461 100644 --- a/src/feature/hs/hs_service.h +++ b/src/feature/hs/hs_service.h @@ -113,7 +113,7 @@ typedef struct hs_service_intropoints_t { digest256map_t *map; /** Contains node's identity key digest that were introduction point for this - * descriptor but were retried to many times. We keep those so we avoid + * descriptor but were retried too many times. We keep those so we avoid * re-picking them over and over for a circuit retry period. * XXX: Once we have #22173, change this to only use ed25519 identity. */ digestmap_t *failed_id; diff --git a/src/feature/relay/dns.c b/src/feature/relay/dns.c index f6a020d061..129f6209d7 100644 --- a/src/feature/relay/dns.c +++ b/src/feature/relay/dns.c @@ -563,6 +563,12 @@ send_resolved_cell,(edge_connection_t *conn, uint8_t answer_type, connection_edge_send_command(conn, RELAY_COMMAND_RESOLVED, buf, buflen); } +void +dns_send_resolved_error_cell(edge_connection_t *conn, uint8_t answer_type) +{ + send_resolved_cell(conn, answer_type, NULL); +} + /** Send a response to the RESOLVE request of a connection for an in-addr.arpa * address on connection <b>conn</b> which yielded the result <b>hostname</b>. * The answer type will be RESOLVED_HOSTNAME. diff --git a/src/feature/relay/dns.h b/src/feature/relay/dns.h index 3f8519bd97..5de70039d4 100644 --- a/src/feature/relay/dns.h +++ b/src/feature/relay/dns.h @@ -20,6 +20,8 @@ int dns_reset(void); void connection_dns_remove(edge_connection_t *conn); void assert_connection_edge_not_dns_pending(edge_connection_t *conn); int dns_resolve(edge_connection_t *exitconn); +void dns_send_resolved_error_cell(edge_connection_t *conn, + uint8_t answer_type); int dns_seems_to_be_broken(void); int dns_seems_to_be_broken_for_ipv6(void); void dns_reset_correctness_checks(void); @@ -36,6 +38,8 @@ void dns_launch_correctness_checks(void); #else /* !defined(HAVE_MODULE_RELAY) */ #define dns_init() (0) +#define dns_send_resolved_error_cell(conn, answer_type) \ + ((void)(conn), (void)(answer_type)) #define dns_seems_to_be_broken() (0) #define has_dns_init_failed() (0) #define dns_cache_total_allocation() (0) diff --git a/src/feature/relay/relay_metrics.c b/src/feature/relay/relay_metrics.c index 8f3b82bd96..8b8c07f580 100644 --- a/src/feature/relay/relay_metrics.c +++ b/src/feature/relay/relay_metrics.c @@ -433,6 +433,12 @@ fill_dos_values(void) metrics_store_entry_add_label(sentry, metrics_format_label("type", "introduce2_rejected")); metrics_store_entry_update(sentry, hs_dos_get_intro2_rejected_count()); + + sentry = metrics_store_add(the_store, rentry->type, rentry->name, + rentry->help, 0, NULL); + metrics_store_entry_add_label(sentry, + metrics_format_label("type", "stream_rejected")); + metrics_store_entry_update(sentry, dos_get_num_stream_rejected()); } /** Fill function for the RELAY_METRICS_CC_COUNTERS metric. */ diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index 8f4f33151a..20610b6011 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -2290,19 +2290,14 @@ typedef struct { /** Keep track of the onionskin requests for an assessment period. */ static overload_onionskin_assessment_t overload_onionskin_assessment; -/** - * We combine ntorv3 and ntor into the same stat, so we must - * use this function to convert the cell type to a stat index. +/** This function ensures that we clamp the maximum value of the given input + * <b>type</b> to NTOR in case the input is out of range. */ static inline uint16_t onionskin_type_to_stat(uint16_t type) { - if (type == ONION_HANDSHAKE_TYPE_NTOR_V3) { - return ONION_HANDSHAKE_TYPE_NTOR; - } - if (BUG(type > MAX_ONION_STAT_TYPE)) { - return MAX_ONION_STAT_TYPE; // use ntor if out of range + return MAX_ONION_STAT_TYPE; // use ntor_v3 if out of range } return type; @@ -2371,7 +2366,8 @@ rep_hist_note_circuit_handshake_requested(uint16_t type) onion_handshakes_requested[stat]++; /* Only relays get to record requested onionskins. */ - if (stat == ONION_HANDSHAKE_TYPE_NTOR) { + if (stat == ONION_HANDSHAKE_TYPE_NTOR || + stat == ONION_HANDSHAKE_TYPE_NTOR_V3) { /* Assess if we've reached the overload general signal. */ overload_general_onionskin_assessment(); @@ -2398,7 +2394,8 @@ rep_hist_note_circuit_handshake_dropped(uint16_t type) stats_n_onionskin_dropped[stat]++; /* Only relays get to record requested onionskins. */ - if (stat == ONION_HANDSHAKE_TYPE_NTOR) { + if (stat == ONION_HANDSHAKE_TYPE_NTOR || + stat == ONION_HANDSHAKE_TYPE_NTOR_V3) { /* Note the dropped ntor in the overload assessment object. */ overload_onionskin_assessment.n_ntor_dropped++; } @@ -2438,11 +2435,13 @@ rep_hist_log_circuit_handshake_stats(time_t now) { (void)now; log_notice(LD_HEARTBEAT, "Circuit handshake stats since last time: " - "%d/%d TAP, %d/%d NTor.", + "%d/%d TAP, %d/%d NTor, %d/%d NTor (v3).", onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_TAP], onion_handshakes_requested[ONION_HANDSHAKE_TYPE_TAP], onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR], - onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR]); + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR], + onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR_V3], + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR_V3]); memset(onion_handshakes_assigned, 0, sizeof(onion_handshakes_assigned)); memset(onion_handshakes_requested, 0, sizeof(onion_handshakes_requested)); } diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index fbfab4c451..a51d81beb9 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -102,8 +102,8 @@ void rep_hist_note_dns_error(int type, uint8_t error); void rep_hist_consensus_has_changed(const networkstatus_t *ns); /** We combine ntor and ntorv3 stats, so we have 3 stat types: - * tap, fast, and ntor. The max type is ntor (2) */ -#define MAX_ONION_STAT_TYPE ONION_HANDSHAKE_TYPE_NTOR + * tap, fast, and ntor. The max type is ntor_v3 (3) */ +#define MAX_ONION_STAT_TYPE MAX_ONION_HANDSHAKE_TYPE extern uint64_t rephist_total_alloc; extern uint32_t rephist_total_num; diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.c b/src/lib/crypt_ops/crypto_openssl_mgt.c index 6c01cb6aa8..ca12a82518 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.c +++ b/src/lib/crypt_ops/crypto_openssl_mgt.c @@ -40,6 +40,11 @@ ENABLE_GCC_WARNING("-Wredundant-decls") #include <string.h> +#ifdef OPENSSL_NO_ENGINE +/* Android's OpenSSL seems to have removed all of its Engine support. */ +#define DISABLE_ENGINES +#endif + #ifndef NEW_THREAD_API /** A number of preallocated mutexes for use by OpenSSL. */ static tor_mutex_t **openssl_mutexes_ = NULL; diff --git a/src/lib/crypt_ops/crypto_openssl_mgt.h b/src/lib/crypt_ops/crypto_openssl_mgt.h index 96a37721dd..eac0ec1977 100644 --- a/src/lib/crypt_ops/crypto_openssl_mgt.h +++ b/src/lib/crypt_ops/crypto_openssl_mgt.h @@ -49,11 +49,6 @@ #define OPENSSL_V_SERIES(a,b,c) \ OPENSSL_VER((a),(b),(c),0,0) -#ifdef OPENSSL_NO_ENGINE -/* Android's OpenSSL seems to have removed all of its Engine support. */ -#define DISABLE_ENGINES -#endif - #if OPENSSL_VERSION_NUMBER >= OPENSSL_VER(1,1,0,0,5) /* OpenSSL as of 1.1.0pre4 has an "new" thread API, which doesn't require * setting up various callbacks. diff --git a/src/lib/log/.may_include b/src/lib/log/.may_include index 54d96324db..09e2b90282 100644 --- a/src/lib/log/.may_include +++ b/src/lib/log/.may_include @@ -9,8 +9,10 @@ lib/intmath/*.h lib/lock/*.h lib/log/*.h lib/malloc/*.h +lib/metrics/*.h lib/string/*.h lib/subsys/*.h lib/testsupport/*.h +lib/thread/threads.h lib/version/*.h lib/wallclock/*.h diff --git a/src/lib/log/log.c b/src/lib/log/log.c index db57ee61a2..7ee4ce86f9 100644 --- a/src/lib/log/log.c +++ b/src/lib/log/log.c @@ -33,6 +33,7 @@ #define LOG_PRIVATE #include "lib/log/log.h" #include "lib/log/log_sys.h" +#include "lib/log/util_bug.h" #include "lib/version/git_revision.h" #include "lib/log/ratelim.h" #include "lib/lock/compat_mutex.h" @@ -912,6 +913,7 @@ init_logging(int disable_startup_queue) { if (!log_mutex_initialized) { tor_mutex_init(&log_mutex); + tor_bug_init_counter(); log_mutex_initialized = 1; } #ifdef __GNUC__ diff --git a/src/lib/log/log_sys.c b/src/lib/log/log_sys.c index 33609f5288..9bae36e1d1 100644 --- a/src/lib/log/log_sys.c +++ b/src/lib/log/log_sys.c @@ -11,11 +11,16 @@ #include "lib/log/escape.h" #include "lib/log/log.h" #include "lib/log/log_sys.h" +#include "lib/log/util_bug.h" +#include "lib/metrics/metrics_store.h" + +static metrics_store_t *the_store; static int subsys_logging_initialize(void) { init_logging(0); + the_store = metrics_store_new(); return 0; } @@ -26,6 +31,29 @@ subsys_logging_shutdown(void) escaped(NULL); } +static const smartlist_t * +logging_metrics_get_stores(void) +{ + static smartlist_t *stores_list = NULL; + + metrics_store_reset(the_store); + + metrics_store_entry_t *sentry = metrics_store_add( + the_store, + METRICS_TYPE_COUNTER, + METRICS_NAME(bug_reached), + "Total number of BUG() and similar assertion reached", + 0, NULL); + metrics_store_entry_update(sentry, tor_bug_get_count()); + + if (!stores_list) { + stores_list = smartlist_new(); + smartlist_add(stores_list, the_store); + } + + return stores_list; +} + const subsys_fns_t sys_logging = { .name = "log", SUBSYS_DECLARE_LOCATION(), @@ -35,4 +63,5 @@ const subsys_fns_t sys_logging = { .level = -90, .initialize = subsys_logging_initialize, .shutdown = subsys_logging_shutdown, + .get_metrics = logging_metrics_get_stores, }; diff --git a/src/lib/log/util_bug.c b/src/lib/log/util_bug.c index 34b41324af..d2dbb0a7a5 100644 --- a/src/lib/log/util_bug.c +++ b/src/lib/log/util_bug.c @@ -18,6 +18,7 @@ #endif #include "lib/malloc/malloc.h" #include "lib/string/printf.h" +#include "lib/thread/threads.h" #include <string.h> #include <stdlib.h> @@ -101,6 +102,27 @@ tor_assertion_failed_(const char *fname, unsigned int line, tor_free(buf); } +static atomic_counter_t total_bug_reached; + +void +tor_bug_init_counter(void) +{ + atomic_counter_init(&total_bug_reached); +} + +/** Helper to update BUG count in metrics. */ +void +tor_bug_increment_count_(void) +{ + atomic_counter_add(&total_bug_reached, 1); +} + +size_t +tor_bug_get_count(void) +{ + return atomic_counter_get(&total_bug_reached); +} + /** Helper for tor_assert_nonfatal: report the assertion failure. */ void tor_bug_occurred_(const char *fname, unsigned int line, @@ -110,6 +132,11 @@ tor_bug_occurred_(const char *fname, unsigned int line, char *buf = NULL; const char *once_str = once ? " (Future instances of this warning will be silenced.)": ""; + if (! once) { + // _once assertions count from the macro directly so we count them as many + // time as they are reached, and not just once. + tor_bug_increment_count_(); + } if (! expr) { if (capturing_bugs()) { add_captured_bug("This line should not have been reached."); diff --git a/src/lib/log/util_bug.h b/src/lib/log/util_bug.h index dd82981e08..5ea198f7ff 100644 --- a/src/lib/log/util_bug.h +++ b/src/lib/log/util_bug.h @@ -39,6 +39,7 @@ #include "orconfig.h" #include "lib/cc/compat_compiler.h" #include "lib/log/log.h" +#include "lib/smartlist_core/smartlist_core.h" #include "lib/testsupport/testsupport.h" /* Replace assert() with a variant that sends failures to the log before @@ -191,6 +192,7 @@ STMT_END #define tor_assert_nonfatal_unreached_once() STMT_BEGIN \ static int warning_logged__ = 0; \ + tor_bug_increment_count_(); \ if (!warning_logged__) { \ warning_logged__ = 1; \ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, NULL, 1, NULL); \ @@ -198,10 +200,12 @@ STMT_END #define tor_assert_nonfatal_once(cond) STMT_BEGIN \ static int warning_logged__ = 0; \ - if (ASSERT_PREDICT_LIKELY_(cond)) { \ - } else if (!warning_logged__) { \ - warning_logged__ = 1; \ - tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1, NULL);\ + if (!ASSERT_PREDICT_LIKELY_(cond)) { \ + tor_bug_increment_count_(); \ + if (!warning_logged__) { \ + warning_logged__ = 1; \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, #cond, 1, NULL);\ + } \ } \ STMT_END #define BUG(cond) \ @@ -215,18 +219,22 @@ if (( { \ static int var = 0; \ int bool_result = !!(cond); \ - if (bool_result && !var) { \ - var = 1; \ - tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ - ("!("#cond")"), 1, NULL); \ + if (bool_result) { \ + tor_bug_increment_count_(); \ + if (!var) { \ + var = 1; \ + tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ + ("!("#cond")"), 1, NULL); \ + } \ } \ bool_result; } )) #else /* !defined(__GNUC__) */ #define IF_BUG_ONCE__(cond,var) \ static int var = 0; \ if ((cond) ? \ - (var ? 1 : \ + (var ? (tor_bug_increment_count_(), 1) : \ (var=1, \ + tor_bug_increment_count_(), \ tor_bug_occurred_(SHORT_FILE__, __LINE__, __func__, \ ("!("#cond")"), 1, NULL), \ 1)) \ @@ -273,12 +281,15 @@ void tor_assertion_failed_(const char *fname, unsigned int line, const char *func, const char *expr, const char *fmt, ...) CHECK_PRINTF(5,6); +void tor_bug_increment_count_(void); +size_t tor_bug_get_count(void); void tor_bug_occurred_(const char *fname, unsigned int line, const char *func, const char *expr, int once, const char *fmt, ...) CHECK_PRINTF(6,7); void tor_abort_(void) ATTR_NORETURN; +void tor_bug_init_counter(void); #ifdef _WIN32 #define SHORT_FILE__ (tor_fix_source_file(__FILE__)) diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c index 8ac07abfc9..08322e17d4 100644 --- a/src/lib/sandbox/sandbox.c +++ b/src/lib/sandbox/sandbox.c @@ -125,6 +125,15 @@ #define SYSCALL_NAME_DEBUGGING #endif +/** + * On newer architectures Linux provides a standardized, generic set of system + * calls (defined in Linux's include/uapi/asm-generic/unistd.h), which omits a + * number of legacy calls used by glibc on other platforms. + */ +#if defined(__aarch64__) || defined(__riscv) +#define ARCH_USES_GENERIC_SYSCALLS +#endif + /**Determines if at least one sandbox is active.*/ static int sandbox_active = 0; /** Holds the parameter list configuration for the sandbox.*/ @@ -263,8 +272,9 @@ static int filter_nopar_gen[] = { #ifdef __NR_sigreturn SCMP_SYS(sigreturn), #endif +#if defined(__NR_stat) SCMP_SYS(stat), -#if defined(__i386__) && defined(__NR_statx) +#elif defined(__i386__) && defined(__NR_statx) SCMP_SYS(statx), #endif SCMP_SYS(uname), @@ -335,6 +345,8 @@ static int filter_nopar_gen[] = { seccomp_rule_add((ctx),(act),(call),3,(f1),(f2),(f3)) #define seccomp_rule_add_4(ctx,act,call,f1,f2,f3,f4) \ seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4)) +#define seccomp_rule_add_5(ctx,act,call,f1,f2,f3,f4,f5) \ + seccomp_rule_add((ctx),(act),(call),4,(f1),(f2),(f3),(f4),(f5)) static const char *sandbox_get_interned_string(const char *str); @@ -516,18 +528,33 @@ is_libc_at_least(int major, int minor) static int libc_uses_openat_for_open(void) { +#ifdef __NR_open return is_libc_at_least(2, 26); +#else + return 1; +#endif /* defined(__NR_open) */ } +/* Calls to opendir() cannot be filtered by the sandbox when built with fragile + * hardening for an architecture that uses Linux's generic syscall interface, + * so prevent a compiler warning by omitting this function along with + * sb_opendir(). */ +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) /* Return true if we think we're running with a libc that uses openat for the * opendir function on linux. */ static int libc_uses_openat_for_opendir(void) { +#ifdef __NR_open // libc 2.27 and above or between 2.15 (inclusive) and 2.22 (exclusive) return is_libc_at_least(2, 27) || (is_libc_at_least(2, 15) && !is_libc_at_least(2, 22)); +#else + return 1; +#endif /* defined(__NR_open) */ } +#endif /* !(defined(ENABLE_FRAGILE_HARDENING) && + defined(ARCH_USES_GENERIC_SYSCALLS)) */ /** Allow a single file to be opened. If <b>use_openat</b> is true, * we're using a libc that remaps all the opens into openats. */ @@ -557,10 +584,25 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) int use_openat = libc_uses_openat_for_open(); #ifdef ENABLE_FRAGILE_HARDENING - /* AddressSanitizer uses the "open" syscall to access information about the - * running process via the filesystem, so that call must be allowed without + /* AddressSanitizer uses either the "open" or the "openat" syscall (depending + * on the architecture) to access information about the running process via + * the filesystem, so the appropriate call must be allowed without * restriction or the sanitizer will be unable to execute normally when the * process terminates. */ +#ifdef ARCH_USES_GENERIC_SYSCALLS + rc = seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), + SCMP_CMP_LOWER32_EQ(0, AT_FDCWD)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " + "libseccomp error %d", rc); + return rc; + } + + /* The "open" syscall is not defined on this architecture, so any other + * requests to open files will necessarily use "openat" as well and there is + * no need to consider any additional rules. */ + return 0; +#else rc = seccomp_rule_add_0(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open)); if (rc != 0) { log_err(LD_BUG,"(Sandbox) failed to add open syscall, received " @@ -572,7 +614,8 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) * there is no need to consider any additional rules. */ if (!use_openat) return 0; -#endif +#endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ +#endif /* defined(ENABLE_FRAGILE_HARDENING) */ // for each dynamic parameter filters for (elem = filter; elem != NULL; elem = elem->next) { @@ -592,6 +635,33 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#ifdef ARCH_USES_GENERIC_SYSCALLS +static int +sb_fchmodat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && param->syscall + == SCMP_SYS(fchmodat)) { + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmodat), + SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), + SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add fchmodat syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} +#else static int sb_chmod(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { @@ -616,9 +686,11 @@ sb_chmod(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ +#if defined(ARCH_USES_GENERIC_SYSCALLS) static int -sb_fchmodat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +sb_fchownat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc; sandbox_cfg_t *elem = NULL; @@ -628,12 +700,12 @@ sb_fchmodat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) smp_param_t *param = elem->param; if (param != NULL && param->prot == 1 && param->syscall - == SCMP_SYS(fchmodat)) { - rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchmodat), + == SCMP_SYS(fchownat)) { + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchownat), SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); if (rc != 0) { - log_err(LD_BUG,"(Sandbox) failed to add fchmodat syscall, received " + log_err(LD_BUG,"(Sandbox) failed to add fchownat syscall, received " "libseccomp error %d", rc); return rc; } @@ -642,8 +714,7 @@ sb_fchmodat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } - -#ifdef __i386__ +#elif defined(__i386__) static int sb_chown32(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { @@ -693,10 +764,15 @@ sb_chown(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } -#endif /* defined(__i386__) */ +#endif /* defined(ARCH_USES_GENERIC_SYSCALLS) || defined(__i386__) */ +#if defined(__NR_rename) +/** + * Function responsible for setting up the rename syscall for + * the seccomp filter sandbox. + */ static int -sb_fchownat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc; sandbox_cfg_t *elem = NULL; @@ -705,13 +781,14 @@ sb_fchownat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) for (elem = filter; elem != NULL; elem = elem->next) { smp_param_t *param = elem->param; - if (param != NULL && param->prot == 1 && param->syscall - == SCMP_SYS(fchownat)) { - rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchownat), - SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), - SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); + if (param != NULL && param->prot == 1 && + param->syscall == SCMP_SYS(rename)) { + + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename), + SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value), + SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value2)); if (rc != 0) { - log_err(LD_BUG,"(Sandbox) failed to add fchownat syscall, received " + log_err(LD_BUG,"(Sandbox) failed to add rename syscall, received " "libseccomp error %d", rc); return rc; } @@ -720,13 +797,13 @@ sb_fchownat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } - +#elif defined(__NR_renameat) /** - * Function responsible for setting up the rename syscall for + * Function responsible for setting up the renameat syscall for * the seccomp filter sandbox. */ static int -sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +sb_renameat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc; sandbox_cfg_t *elem = NULL; @@ -736,13 +813,15 @@ sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) smp_param_t *param = elem->param; if (param != NULL && param->prot == 1 && - param->syscall == SCMP_SYS(rename)) { + param->syscall == SCMP_SYS(renameat)) { - rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rename), - SCMP_CMP_STR(0, SCMP_CMP_EQ, param->value), - SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value2)); + rc = seccomp_rule_add_4(ctx, SCMP_ACT_ALLOW, SCMP_SYS(renameat), + SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), + SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), + SCMP_CMP_LOWER32_EQ(2, AT_FDCWD), + SCMP_CMP_STR(3, SCMP_CMP_EQ, param->value2)); if (rc != 0) { - log_err(LD_BUG,"(Sandbox) failed to add rename syscall, received " + log_err(LD_BUG,"(Sandbox) failed to add renameat syscall, received " "libseccomp error %d", rc); return rc; } @@ -751,13 +830,13 @@ sb_rename(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } - +#else /** - * Function responsible for setting up the renameat syscall for + * Function responsible for setting up the renameat2 syscall for * the seccomp filter sandbox. */ static int -sb_renameat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +sb_renameat2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) { int rc; sandbox_cfg_t *elem = NULL; @@ -767,15 +846,16 @@ sb_renameat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) smp_param_t *param = elem->param; if (param != NULL && param->prot == 1 && - param->syscall == SCMP_SYS(renameat)) { + param->syscall == SCMP_SYS(renameat2)) { - rc = seccomp_rule_add_4(ctx, SCMP_ACT_ALLOW, SCMP_SYS(renameat), + rc = seccomp_rule_add_5(ctx, SCMP_ACT_ALLOW, SCMP_SYS(renameat2), SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), SCMP_CMP_LOWER32_EQ(2, AT_FDCWD), - SCMP_CMP_STR(3, SCMP_CMP_EQ, param->value2)); + SCMP_CMP_STR(3, SCMP_CMP_EQ, param->value2), + SCMP_CMP(4, SCMP_CMP_EQ, 0)); if (rc != 0) { - log_err(LD_BUG,"(Sandbox) failed to add renameat syscall, received " + log_err(LD_BUG,"(Sandbox) failed to add renameat2 syscall, received " "libseccomp error %d", rc); return rc; } @@ -784,7 +864,19 @@ sb_renameat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#endif /* defined(__NR_rename) || defined(__NR_renameat) */ +/* If Tor is built with fragile hardening for an architecture that uses Linux's + * generic syscall interface a rule allowing the "openat" syscall without + * restriction will have already been added by sb_open(), so there is no need + * to consider adding additional, more restrictive rules here as they will + * simply be ignored. + * + * Also, since the "open" syscall is not defined on these architectures, glibc + * will necessarily use "openat" for its implementation of opendir() as well. + * This means neither of the following two functions will have any effect and + * both can be omitted. */ +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) /** * Function responsible for setting up the openat syscall for * the seccomp filter sandbox. @@ -840,6 +932,8 @@ sb_opendir(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#endif /* !(defined(ENABLE_FRAGILE_HARDENING) && + defined(ARCH_USES_GENERIC_SYSCALLS)) */ #ifdef ENABLE_FRAGILE_HARDENING /** @@ -859,9 +953,17 @@ sb_ptrace(scmp_filter_ctx ctx, sandbox_cfg_t *filter) if (rc) return rc; + /* AddressSanitizer uses "PTRACE_GETREGSET" on AArch64 (ARM64) and + * System/390, "PTRACE_GETREGS" everywhere else. */ +#if defined(__aarch64__) || defined(__s390__) + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), + SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGSET), + SCMP_CMP(1, SCMP_CMP_EQ, pid)); +#else rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ptrace), SCMP_CMP(0, SCMP_CMP_EQ, PTRACE_GETREGS), SCMP_CMP(1, SCMP_CMP_EQ, pid)); +#endif /* defined(__aarch64__) || defined(__s390__) */ if (rc) return rc; @@ -1351,6 +1453,40 @@ sb_mremap(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +#ifdef ARCH_USES_GENERIC_SYSCALLS +/** + * Function responsible for setting up the newfstatat syscall for + * the seccomp filter sandbox. + */ +static int +sb_newfstatat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc = 0; + + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && (param->syscall == SCMP_SYS(open) + || param->syscall == PHONY_OPENDIR_SYSCALL + || param->syscall == SCMP_SYS(newfstatat))) { + rc = seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), + SCMP_CMP_LOWER32_EQ(0, AT_FDCWD), + SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value)); + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add newfstatat syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} +#endif /* defined(ARCH_USES_GENERIC_SYSCALLS) */ + #ifdef __NR_stat64 /** * Function responsible for setting up the stat64 syscall for @@ -1409,22 +1545,33 @@ static sandbox_filter_func_t filter_func[] = { #ifdef __NR_mmap2 sb_mmap2, #endif -#ifdef __i386__ +#if defined(ARCH_USES_GENERIC_SYSCALLS) + sb_fchownat, +#elif defined(__i386__) sb_chown32, #else sb_chown, #endif - sb_fchownat, - sb_chmod, +#if defined(ARCH_USES_GENERIC_SYSCALLS) sb_fchmodat, +#else + sb_chmod, +#endif sb_open, +#if !(defined(ENABLE_FRAGILE_HARDENING) && defined(ARCH_USES_GENERIC_SYSCALLS)) sb_openat, sb_opendir, +#endif #ifdef ENABLE_FRAGILE_HARDENING sb_ptrace, #endif +#if defined(__NR_rename) sb_rename, +#elif defined(__NR_renameat) sb_renameat, +#else + sb_renameat2, +#endif #ifdef __NR_fcntl64 sb_fcntl64, #endif @@ -1434,7 +1581,9 @@ static sandbox_filter_func_t filter_func[] = { sb_flock, sb_futex, sb_mremap, -#ifdef __NR_stat64 +#if defined(ARCH_USES_GENERIC_SYSCALLS) + sb_newfstatat, +#elif defined(__NR_stat64) sb_stat64, #endif @@ -1690,27 +1839,31 @@ new_element(int syscall, char *value) return new_element2(syscall, value, NULL); } -#ifdef __i386__ -#define SCMP_chown SCMP_SYS(chown32) -#elif defined(__aarch64__) && defined(__LP64__) +#if defined(ARCH_USES_GENERIC_SYSCALLS) #define SCMP_chown SCMP_SYS(fchownat) +#elif defined(__i386__) +#define SCMP_chown SCMP_SYS(chown32) #else #define SCMP_chown SCMP_SYS(chown) #endif -#if defined(__aarch64__) && defined(__LP64__) +#if defined(ARCH_USES_GENERIC_SYSCALLS) #define SCMP_chmod SCMP_SYS(fchmodat) #else #define SCMP_chmod SCMP_SYS(chmod) #endif -#if defined(__aarch64__) && defined(__LP64__) +#if defined(__NR_rename) +#define SCMP_rename SCMP_SYS(rename) +#elif defined(__NR_renameat) #define SCMP_rename SCMP_SYS(renameat) #else -#define SCMP_rename SCMP_SYS(rename) +#define SCMP_rename SCMP_SYS(renameat2) #endif -#ifdef __NR_stat64 +#if defined(ARCH_USES_GENERIC_SYSCALLS) +#define SCMP_stat SCMP_SYS(newfstatat) +#elif defined(__NR_stat64) #define SCMP_stat SCMP_SYS(stat64) #else #define SCMP_stat SCMP_SYS(stat) diff --git a/src/test/test_conflux_pool.c b/src/test/test_conflux_pool.c index fc30677377..05dc7b14ff 100644 --- a/src/test/test_conflux_pool.c +++ b/src/test/test_conflux_pool.c @@ -396,6 +396,7 @@ test_setup(void) static void test_clear_circs(void) { + conflux_notify_shutdown(); SMARTLIST_FOREACH(circ_pairs, circ_pair_t *, circ_pair, { tor_free(circ_pair); }); @@ -430,6 +431,9 @@ test_clear_circs(void) tor_assert(smartlist_len(mock_cell_delivery) == 0); (void)free_fake_origin_circuit; + + /* Clear shutdown flag so we can resume testing again. */ + conflux_clear_shutdown(); } static void diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 248fd8ab5d..87aaef7fe6 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -4078,7 +4078,7 @@ gen_routerstatus_for_umbw(int idx, time_t now) if (vrs) { vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t)); tor_asprintf(&vrs->microdesc->microdesc_hash_line, - "m 25,26,27,28 " + "m 32,33 " "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n", idx); } @@ -4102,9 +4102,8 @@ vote_tweaks_for_umbw(networkstatus_t *v, int voter, time_t now) tt_assert(v->supported_methods); SMARTLIST_FOREACH(v->supported_methods, char *, c, tor_free(c)); smartlist_clear(v->supported_methods); - /* Method 17 is MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB */ smartlist_split_string(v->supported_methods, - "25 26 27 28", + "32 33", NULL, 0, -1); /* If we're using a non-default clip bandwidth, add it to net_params */ if (alternate_clip_bw > 0) { diff --git a/src/test/test_dirvote.c b/src/test/test_dirvote.c index 2b53955107..bb7e6fdf10 100644 --- a/src/test/test_dirvote.c +++ b/src/test/test_dirvote.c @@ -656,30 +656,6 @@ done: ROUTER_FREE(pppp); } -static void -test_dirvote_parse_param_buggy(void *arg) -{ - (void)arg; - - /* Tests for behavior with bug emulation to migrate away from bug 19011. */ - tt_i64_op(extract_param_buggy("blah blah", "bwweightscale", 10000), - OP_EQ, 10000); - tt_i64_op(extract_param_buggy("bwweightscale=7", "bwweightscale", 10000), - OP_EQ, 7); - tt_i64_op(extract_param_buggy("bwweightscale=7 foo=9", - "bwweightscale", 10000), - OP_EQ, 10000); - tt_i64_op(extract_param_buggy("foo=7 bwweightscale=777 bar=9", - "bwweightscale", 10000), - OP_EQ, 10000); - tt_i64_op(extract_param_buggy("foo=7 bwweightscale=1234", - "bwweightscale", 10000), - OP_EQ, 1234); - - done: - ; -} - #define NODE(name, flags) \ { \ #name, test_dirvote_##name, (flags), NULL, NULL \ @@ -692,5 +668,4 @@ struct testcase_t dirvote_tests[] = { NODE(get_sybil_by_ip_version_ipv4, TT_FORK), NODE(get_sybil_by_ip_version_ipv6, TT_FORK), NODE(get_all_possible_sybil, TT_FORK), - NODE(parse_param_buggy, 0), END_OF_TESTCASES}; diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index c564805ecf..315a38f257 100644 --- a/src/test/test_microdesc.c +++ b/src/test/test_microdesc.c @@ -366,37 +366,14 @@ static const char test_ri[] = "iFJkKxxDx7ksxX0zdl7aPT4ORFEuRhCYS6el7YJmoyg=\n" "-----END SIGNATURE-----\n"; -static const char test_md2_25[] = +static const char test_md2_withfamily_33[] = "onion-key\n" "-----BEGIN RSA PUBLIC KEY-----\n" "MIGJAoGBAMvEJ/JVNK7I38PPWhQMuCgkET/ki4WIas4tj5Kmqfb9kHqxMR+EunRD\n" "83k4pel1yB7QdV+iTd/4SZOI8RpZP+BO1KnOTWfpztAU1lDGr19/PwdwcHaILpBD\n" "nNzm6otk4/bKUQ0vqpOfJljtg0DfAm4uMAQ6BMFy6uEAF7+JupuPAgMBAAE=\n" "-----END RSA PUBLIC KEY-----\n" - "ntor-onion-key FChIfm77vrWB7JsxQ+jMbN6VSSp1P0DYbw/2aqey4iA=\n" - "p accept 1-65535\n" - "id ed25519 J5lkRqyL6qW+CpN3E4RIlgJZeLgwjtmOOrjZvVhuwLQ\n"; - -static const char test_md2_withfamily_28[] = - "onion-key\n" - "-----BEGIN RSA PUBLIC KEY-----\n" - "MIGJAoGBAMvEJ/JVNK7I38PPWhQMuCgkET/ki4WIas4tj5Kmqfb9kHqxMR+EunRD\n" - "83k4pel1yB7QdV+iTd/4SZOI8RpZP+BO1KnOTWfpztAU1lDGr19/PwdwcHaILpBD\n" - "nNzm6otk4/bKUQ0vqpOfJljtg0DfAm4uMAQ6BMFy6uEAF7+JupuPAgMBAAE=\n" - "-----END RSA PUBLIC KEY-----\n" - "ntor-onion-key FChIfm77vrWB7JsxQ+jMbN6VSSp1P0DYbw/2aqey4iA=\n" - "family OtherNode !Strange\n" - "p accept 1-65535\n" - "id ed25519 J5lkRqyL6qW+CpN3E4RIlgJZeLgwjtmOOrjZvVhuwLQ\n"; - -static const char test_md2_withfamily_29[] = - "onion-key\n" - "-----BEGIN RSA PUBLIC KEY-----\n" - "MIGJAoGBAMvEJ/JVNK7I38PPWhQMuCgkET/ki4WIas4tj5Kmqfb9kHqxMR+EunRD\n" - "83k4pel1yB7QdV+iTd/4SZOI8RpZP+BO1KnOTWfpztAU1lDGr19/PwdwcHaILpBD\n" - "nNzm6otk4/bKUQ0vqpOfJljtg0DfAm4uMAQ6BMFy6uEAF7+JupuPAgMBAAE=\n" - "-----END RSA PUBLIC KEY-----\n" - "ntor-onion-key FChIfm77vrWB7JsxQ+jMbN6VSSp1P0DYbw/2aqey4iA=\n" + "ntor-onion-key FChIfm77vrWB7JsxQ+jMbN6VSSp1P0DYbw/2aqey4iA\n" "family !Strange $D219590AC9513BCDEBBA9AB721007A4CC01BBAE3 othernode\n" "p accept 1-65535\n" "id ed25519 J5lkRqyL6qW+CpN3E4RIlgJZeLgwjtmOOrjZvVhuwLQ\n"; @@ -411,21 +388,12 @@ test_md_generate(void *arg) ri = router_parse_entry_from_string(test_ri, NULL, 0, 0, NULL, NULL); tt_assert(ri); - md = dirvote_create_microdescriptor(ri, 25); - tt_str_op(md->body, OP_EQ, test_md2_25); - tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey, - &ri->cache_info.signing_key_cert->signing_key)); - // Try family encoding. microdesc_free(md); ri->declared_family = smartlist_new(); smartlist_add_strdup(ri->declared_family, "OtherNode !Strange"); - md = dirvote_create_microdescriptor(ri, 28); - tt_str_op(md->body, OP_EQ, test_md2_withfamily_28); - - microdesc_free(md); - md = dirvote_create_microdescriptor(ri, 29); - tt_str_op(md->body, OP_EQ, test_md2_withfamily_29); + md = dirvote_create_microdescriptor(ri, 33); + tt_str_op(md->body, OP_EQ, test_md2_withfamily_33); done: microdesc_free(md); diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c index 64182ecc91..3104790723 100644 --- a/src/test/test_sandbox.c +++ b/src/test/test_sandbox.c @@ -364,22 +364,22 @@ test_sandbox_crypto_equix(void *arg) struct testcase_t sandbox_tests[] = { SANDBOX_TEST(is_active, TT_FORK), -/* When Tor is built with fragile compiler-hardening the sandbox is unable to - * filter requests to open files or directories (on systems where glibc uses - * the "open" system call to provide this functionality), as doing so would +/* When Tor is built with fragile compiler-hardening the sandbox is usually + * unable to filter requests to open files or directories, as doing so would * interfere with the address sanitizer as it retrieves information about the * running process via the filesystem. Skip these tests in that case as the * corresponding functions are likely to have no effect and this will cause the * tests to fail. */ #ifdef ENABLE_FRAGILE_HARDENING SANDBOX_TEST_SKIPPED(open_filename), + SANDBOX_TEST_SKIPPED(openat_filename), SANDBOX_TEST_SKIPPED(opendir_dirname), #else SANDBOX_TEST_IN_SANDBOX(open_filename), + SANDBOX_TEST_IN_SANDBOX(openat_filename), SANDBOX_TEST_IN_SANDBOX(opendir_dirname), #endif /* defined(ENABLE_FRAGILE_HARDENING) */ - SANDBOX_TEST_IN_SANDBOX(openat_filename), SANDBOX_TEST_IN_SANDBOX(chmod_filename), SANDBOX_TEST_IN_SANDBOX(chown_filename), SANDBOX_TEST_IN_SANDBOX(rename_filename), diff --git a/src/test/test_status.c b/src/test/test_status.c index 1d371645ae..4ceb81f3a5 100644 --- a/src/test/test_status.c +++ b/src/test/test_status.c @@ -333,10 +333,12 @@ test_status_hb_not_in_consensus(void *arg) status_hb_not_in_consensus_server_mode); log_global_min_severity_ = LOG_DEBUG; - onion_handshakes_requested[ONION_HANDSHAKE_TYPE_TAP] = 1; onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_TAP] = 1; - onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR] = 1; - onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR] = 1; + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_TAP] = 2; + onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR] = 3; + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR] = 4; + onion_handshakes_assigned[ONION_HANDSHAKE_TYPE_NTOR_V3] = 5; + onion_handshakes_requested[ONION_HANDSHAKE_TYPE_NTOR_V3] = 6; expected = 0; setup_capture_of_logs(LOG_INFO); @@ -352,8 +354,8 @@ test_status_hb_not_in_consensus(void *arg) "I've made 0 connections with IPv4 and 0 with IPv6.\n"); expect_log_msg("Average packaged cell fullness: 100.000%. " "TLS write overhead: 0%\n"); - expect_log_msg("Circuit handshake stats since last time: 1/1 TAP, " - "1/1 NTor.\n"); + expect_log_msg("Circuit handshake stats since last time: 1/2 TAP, " + "3/4 NTor, 5/6 NTor (v3).\n"); expect_log_msg("Since startup we initiated 0 and received 0 v1 " "connections; initiated 0 and received 0 v2 connections; " "initiated 0 and received 0 v3 connections; " @@ -363,6 +365,7 @@ test_status_hb_not_in_consensus(void *arg) "with too many cells, [DoSCircuitCreationEnabled disabled], " "[DoSConnectionEnabled disabled], " "[DoSRefuseSingleHopClientRendezvous disabled], " + "[DoSStreamCreationEnabled disabled], " "0 INTRODUCE2 rejected.\n"); tt_int_op(mock_saved_log_n_entries(), OP_EQ, 6); |