aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/log.c2
-rw-r--r--src/common/torlog.h4
-rw-r--r--src/or/config.c25
-rw-r--r--src/or/dos.c289
-rw-r--r--src/or/dos.h120
-rw-r--r--src/or/include.am2
-rw-r--r--src/or/main.c2
-rw-r--r--src/or/networkstatus.c13
-rw-r--r--src/or/or.h30
9 files changed, 483 insertions, 4 deletions
diff --git a/src/common/log.c b/src/common/log.c
index 56adc77f84..4db1c9f0d0 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -1177,7 +1177,7 @@ static const char *domain_list[] = {
"GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM",
"HTTP", "APP", "CONTROL", "CIRC", "REND", "BUG", "DIR", "DIRSERV",
"OR", "EDGE", "ACCT", "HIST", "HANDSHAKE", "HEARTBEAT", "CHANNEL",
- "SCHED", NULL
+ "SCHED", "DOS", NULL
};
/** Return a bitmask for the log domain for which <b>domain</b> is the name,
diff --git a/src/common/torlog.h b/src/common/torlog.h
index 6732a42741..20b7d938f0 100644
--- a/src/common/torlog.h
+++ b/src/common/torlog.h
@@ -99,8 +99,10 @@
#define LD_CHANNEL (1u<<21)
/** Scheduler */
#define LD_SCHED (1u<<22)
+/** Denial of Service mitigation. */
+#define LD_DOS (1u<<23)
/** Number of logging domains in the code. */
-#define N_LOGGING_DOMAINS 23
+#define N_LOGGING_DOMAINS 24
/** This log message is not safe to send to a callback-based logger
* immediately. Used as a flag, not a log domain. */
diff --git a/src/or/config.c b/src/or/config.c
index 42ff25877e..c651c202ec 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -29,6 +29,7 @@
#include "dirserv.h"
#include "dirvote.h"
#include "dns.h"
+#include "dos.h"
#include "entrynodes.h"
#include "geoip.h"
#include "hibernate.h"
@@ -241,6 +242,19 @@ static config_var_t option_vars_[] = {
OBSOLETE("DynamicDHGroups"),
VPORT(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
+ /* DoS circuit creation options. */
+ V(DoSCircuitCreationEnabled, AUTOBOOL, "auto"),
+ V(DoSCircuitCreationMinConnections, UINT, "0"),
+ V(DoSCircuitCreationRateTenths, UINT, "0"),
+ V(DoSCircuitCreationBurst, UINT, "0"),
+ V(DoSCircuitCreationDefenseType, INT, "0"),
+ V(DoSCircuitCreationDefenseTimePeriod, INTERVAL, "0"),
+ /* DoS connection options. */
+ V(DoSConnectionEnabled, AUTOBOOL, "auto"),
+ V(DoSConnectionMaxConcurrentCount, UINT, "0"),
+ V(DoSConnectionDefenseType, INT, "0"),
+ /* DoS single hop client options. */
+ V(DoSRefuseSingleHopClientRendezvous, AUTOBOOL, "auto"),
V(DownloadExtraInfo, BOOL, "0"),
V(TestingEnableConnBwEvent, BOOL, "0"),
V(TestingEnableCellStatsEvent, BOOL, "0"),
@@ -2039,6 +2053,17 @@ options_act(const or_options_t *old_options)
}
}
+ /* DoS mitigation subsystem only applies to public relay. */
+ if (public_server_mode(options)) {
+ /* If we are configured as a relay, initialize the subsystem. Even on HUP,
+ * this is safe to call as it will load data from the current options
+ * or/and the consensus. */
+ dos_init();
+ } else if (old_options && public_server_mode(old_options)) {
+ /* Going from relay to non relay, clean it up. */
+ dos_free_all();
+ }
+
/* Load the webpage we're going to serve every time someone asks for '/' on
our DirPort. */
tor_free(global_dirfrontpagecontents);
diff --git a/src/or/dos.c b/src/or/dos.c
new file mode 100644
index 0000000000..4b5983d16d
--- /dev/null
+++ b/src/or/dos.c
@@ -0,0 +1,289 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * \file dos.c
+ * \brief Implement Denial of Service mitigation subsystem.
+ */
+
+#define DOS_PRIVATE
+
+#include "or.h"
+#include "channel.h"
+#include "config.h"
+#include "geoip.h"
+#include "main.h"
+#include "networkstatus.h"
+
+#include "dos.h"
+
+/*
+ * Circuit creation denial of service mitigation.
+ *
+ * Namespace used for this mitigation framework is "dos_cc_" where "cc" is for
+ * Circuit Creation.
+ */
+
+/* Is the circuit creation DoS mitigation enabled? */
+static unsigned int dos_cc_enabled = 0;
+
+/* Consensus parameters. They can be changed when a new consensus arrives.
+ * They are initialized with the hardcoded default values. */
+static uint32_t dos_cc_min_concurrent_conn;
+static uint32_t dos_cc_circuit_rate_tenths;
+static uint32_t dos_cc_circuit_burst;
+static dos_cc_defense_type_t dos_cc_defense_type;
+static int32_t dos_cc_defense_time_period;
+
+/*
+ * Concurrent connection denial of service mitigation.
+ *
+ * Namespace used for this mitigation framework is "dos_conn_".
+ */
+
+/* Is the connection DoS mitigation enabled? */
+static unsigned int dos_conn_enabled = 0;
+
+/* Consensus parameters. They can be changed when a new consensus arrives.
+ * They are initialized with the hardcoded default values. */
+static uint32_t dos_conn_max_concurrent_count;
+static dos_conn_defense_type_t dos_conn_defense_type;
+
+/*
+ * General interface of the denial of service mitigation subsystem.
+ */
+
+/* Return true iff the circuit creation mitigation is enabled. We look at the
+ * consensus for this else a default value is returned. */
+MOCK_IMPL(STATIC unsigned int,
+get_param_cc_enabled, (const networkstatus_t *ns))
+{
+ if (get_options()->DoSCircuitCreationEnabled != -1) {
+ return get_options()->DoSCircuitCreationEnabled;
+ }
+
+ return !!networkstatus_get_param(ns, "DoSCircuitCreationEnabled",
+ DOS_CC_ENABLED_DEFAULT, 0, 1);
+}
+
+/* Return the parameter for the minimum concurrent connection at which we'll
+ * start counting circuit for a specific client address. */
+STATIC uint32_t
+get_param_cc_min_concurrent_connection(const networkstatus_t *ns)
+{
+ if (get_options()->DoSCircuitCreationMinConnections) {
+ return get_options()->DoSCircuitCreationMinConnections;
+ }
+ return networkstatus_get_param(ns, "DoSCircuitCreationMinConnections",
+ DOS_CC_MIN_CONCURRENT_CONN_DEFAULT,
+ 1, INT32_MAX);
+}
+
+/* Return the parameter for the time rate that is how many circuits over this
+ * time span. */
+static uint32_t
+get_param_cc_circuit_rate_tenths(const networkstatus_t *ns)
+{
+ /* This is in seconds. */
+ if (get_options()->DoSCircuitCreationRateTenths) {
+ return get_options()->DoSCircuitCreationRateTenths;
+ }
+ return networkstatus_get_param(ns, "DoSCircuitCreationRateTenths",
+ DOS_CC_CIRCUIT_RATE_TENTHS_DEFAULT,
+ 1, INT32_MAX);
+}
+
+/* Return the parameter for the maximum circuit count for the circuit time
+ * rate. */
+STATIC uint32_t
+get_param_cc_circuit_burst(const networkstatus_t *ns)
+{
+ if (get_options()->DoSCircuitCreationBurst) {
+ return get_options()->DoSCircuitCreationBurst;
+ }
+ return networkstatus_get_param(ns, "DoSCircuitCreationBurst",
+ DOS_CC_CIRCUIT_BURST_DEFAULT,
+ 1, INT32_MAX);
+}
+
+/* Return the consensus parameter of the circuit creation defense type. */
+static uint32_t
+get_param_cc_defense_type(const networkstatus_t *ns)
+{
+ if (get_options()->DoSCircuitCreationDefenseType) {
+ return get_options()->DoSCircuitCreationDefenseType;
+ }
+ return networkstatus_get_param(ns, "DoSCircuitCreationDefenseType",
+ DOS_CC_DEFENSE_TYPE_DEFAULT,
+ DOS_CC_DEFENSE_NONE, DOS_CC_DEFENSE_MAX);
+}
+
+/* Return the consensus parameter of the defense time period which is how much
+ * time should we defend against a malicious client address. */
+static int32_t
+get_param_cc_defense_time_period(const networkstatus_t *ns)
+{
+ /* Time in seconds. */
+ if (get_options()->DoSCircuitCreationDefenseTimePeriod) {
+ return get_options()->DoSCircuitCreationDefenseTimePeriod;
+ }
+ return networkstatus_get_param(ns, "DoSCircuitCreationDefenseTimePeriod",
+ DOS_CC_DEFENSE_TIME_PERIOD_DEFAULT,
+ 0, INT32_MAX);
+}
+
+/* Return true iff connection mitigation is enabled. We look at the consensus
+ * for this else a default value is returned. */
+MOCK_IMPL(STATIC unsigned int,
+get_param_conn_enabled, (const networkstatus_t *ns))
+{
+ if (get_options()->DoSConnectionEnabled != -1) {
+ return get_options()->DoSConnectionEnabled;
+ }
+ return !!networkstatus_get_param(ns, "DoSConnectionEnabled",
+ DOS_CONN_ENABLED_DEFAULT, 0, 1);
+}
+
+/* Return the consensus parameter for the maximum concurrent connection
+ * allowed. */
+STATIC uint32_t
+get_param_conn_max_concurrent_count(const networkstatus_t *ns)
+{
+ if (get_options()->DoSConnectionMaxConcurrentCount) {
+ return get_options()->DoSConnectionMaxConcurrentCount;
+ }
+ return networkstatus_get_param(ns, "DoSConnectionMaxConcurrentCount",
+ DOS_CONN_MAX_CONCURRENT_COUNT_DEFAULT,
+ 1, INT32_MAX);
+}
+
+/* Return the consensus parameter of the connection defense type. */
+static uint32_t
+get_param_conn_defense_type(const networkstatus_t *ns)
+{
+ if (get_options()->DoSConnectionDefenseType) {
+ return get_options()->DoSConnectionDefenseType;
+ }
+ return networkstatus_get_param(ns, "DoSConnectionDefenseType",
+ DOS_CONN_DEFENSE_TYPE_DEFAULT,
+ DOS_CONN_DEFENSE_NONE, DOS_CONN_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. */
+static void
+set_dos_parameters(const networkstatus_t *ns)
+{
+ /* Get the default consensus param values. */
+ dos_cc_enabled = get_param_cc_enabled(ns);
+ dos_cc_min_concurrent_conn = get_param_cc_min_concurrent_connection(ns);
+ dos_cc_circuit_rate_tenths = get_param_cc_circuit_rate_tenths(ns);
+ dos_cc_circuit_burst = get_param_cc_circuit_burst(ns);
+ dos_cc_defense_time_period = get_param_cc_defense_time_period(ns);
+ dos_cc_defense_type = get_param_cc_defense_type(ns);
+
+ /* Connection detection. */
+ dos_conn_enabled = get_param_conn_enabled(ns);
+ dos_conn_max_concurrent_count = get_param_conn_max_concurrent_count(ns);
+ dos_conn_defense_type = get_param_conn_defense_type(ns);
+}
+
+/* Free everything for the circuit creation DoS mitigation subsystem. */
+static void
+cc_free_all(void)
+{
+ /* If everything is freed, the circuit creation subsystem is not enabled. */
+ dos_cc_enabled = 0;
+}
+
+/* Called when the consensus has changed. Do appropriate actions for the
+ * circuit creation subsystem. */
+static void
+cc_consensus_has_changed(const networkstatus_t *ns)
+{
+ /* Looking at the consensus, is the circuit creation subsystem enabled? If
+ * not and it was enabled before, clean it up. */
+ if (dos_cc_enabled && !get_param_cc_enabled(ns)) {
+ cc_free_all();
+ }
+}
+
+/* Concurrent connection private API. */
+
+/* Free everything for the connection DoS mitigation subsystem. */
+static void
+conn_free_all(void)
+{
+ dos_conn_enabled = 0;
+}
+
+/* Called when the consensus has changed. Do appropriate actions for the
+ * connection mitigation subsystem. */
+static void
+conn_consensus_has_changed(const networkstatus_t *ns)
+{
+ /* Looking at the consensus, is the connection mitigation subsystem enabled?
+ * If not and it was enabled before, clean it up. */
+ if (dos_conn_enabled && !get_param_conn_enabled(ns)) {
+ conn_free_all();
+ }
+}
+
+/* General private API */
+
+/* Return true iff we have at least one DoS detection enabled. This is used to
+ * decide if we need to allocate any kind of high level DoS object. */
+static inline int
+dos_is_enabled(void)
+{
+ return (dos_cc_enabled || dos_conn_enabled);
+}
+
+/* Circuit creation public API. */
+
+/* Concurrent connection detection public API. */
+
+/* General API */
+
+/* Called when the consensus has changed. We might have new consensus
+ * parameters to look at. */
+void
+dos_consensus_has_changed(const networkstatus_t *ns)
+{
+ cc_consensus_has_changed(ns);
+ conn_consensus_has_changed(ns);
+
+ /* We were already enabled or we just became enabled but either way, set the
+ * consensus parameters for all subsystems. */
+ set_dos_parameters(ns);
+}
+
+/* Return true iff the DoS mitigation subsystem is enabled. */
+int
+dos_enabled(void)
+{
+ return dos_is_enabled();
+}
+
+/* Free everything from the Denial of Service subsystem. */
+void
+dos_free_all(void)
+{
+ /* Free the circuit creation mitigation subsystem. It is safe to do this
+ * even if it wasn't initialized. */
+ cc_free_all();
+
+ /* Free the connection mitigation subsystem. It is safe to do this even if
+ * it wasn't initialized. */
+ conn_free_all();
+}
+
+/* Initialize the Denial of Service subsystem. */
+void
+dos_init(void)
+{
+ /* To initialize, we only need to get the parameters. */
+ set_dos_parameters(NULL);
+}
+
diff --git a/src/or/dos.h b/src/or/dos.h
new file mode 100644
index 0000000000..dc36aaa406
--- /dev/null
+++ b/src/or/dos.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/*
+ * \file dos.h
+ * \brief Header file for dos.c
+ */
+
+#ifndef TOR_DOS_H
+#define TOR_DOS_H
+
+/* Structure that keeps stats of client connection per-IP. */
+typedef struct cc_client_stats_t {
+ /* Number of allocated circuits remaining for this address. It is
+ * decremented every time a new circuit is seen for this client address and
+ * if the count goes to 0, we have a positive detection. */
+ uint32_t circuit_bucket;
+
+ /* When was the last time we've refilled the circuit bucket? This is used to
+ * know if we need to refill the bucket when a new circuit is seen. It is
+ * synchronized using approx_time(). */
+ time_t last_circ_bucket_refill_ts;
+
+ /* This client address was detected to be above the circuit creation rate
+ * and this timestamp indicates until when it should remain marked as
+ * detected so we can apply a defense for the address. It is synchronized
+ * using the approx_time(). */
+ time_t marked_until_ts;
+} cc_client_stats_t;
+
+/* This object is a top level object that contains everything related to the
+ * per-IP client DoS mitigation. Because it is per-IP, it is used in the geoip
+ * clientmap_entry_t object. */
+typedef struct dos_client_stats_t {
+ /* Concurrent connection count from the specific address. 2^32 is most
+ * likely way too big for the amount of allowed file descriptors. */
+ uint32_t concurrent_count;
+
+ /* Circuit creation statistics. This is only used if the circuit creation
+ * subsystem has been enabled (dos_cc_enabled). */
+ cc_client_stats_t cc_stats;
+} dos_client_stats_t;
+
+/* General API. */
+
+void dos_init(void);
+void dos_free_all(void);
+void dos_consensus_has_changed(const networkstatus_t *ns);
+int dos_enabled(void);
+
+/*
+ * Circuit creation DoS mitigation subsystemn interface.
+ */
+
+/* DoSCircuitCreationEnabled default. Disabled by default. */
+#define DOS_CC_ENABLED_DEFAULT 0
+/* DoSCircuitCreationDefenseType maps to the dos_cc_defense_type_t enum. */
+#define DOS_CC_DEFENSE_TYPE_DEFAULT DOS_CC_DEFENSE_REFUSE_CELL
+/* DoSCircuitCreationMinConnections default */
+#define DOS_CC_MIN_CONCURRENT_CONN_DEFAULT 3
+/* DoSCircuitCreationRateTenths is 3 per seconds. */
+#define DOS_CC_CIRCUIT_RATE_TENTHS_DEFAULT (3 * 10)
+/* DoSCircuitCreationBurst default. */
+#define DOS_CC_CIRCUIT_BURST_DEFAULT 90
+/* DoSCircuitCreationDefenseTimePeriod in seconds. */
+#define DOS_CC_DEFENSE_TIME_PERIOD_DEFAULT (60 * 60)
+
+/* Type of defense that we can use for the circuit creation DoS mitigation. */
+typedef enum dos_cc_defense_type_t {
+ /* No defense used. */
+ DOS_CC_DEFENSE_NONE = 1,
+ /* Refuse any cells which means a DESTROY cell will be sent back. */
+ DOS_CC_DEFENSE_REFUSE_CELL = 2,
+
+ /* Maximum value that can be used. Useful for the boundaries of the
+ * consensus parameter. */
+ DOS_CC_DEFENSE_MAX = 2,
+} dos_cc_defense_type_t;
+
+/*
+ * Concurrent connection DoS mitigation interface.
+ */
+
+/* DoSConnectionEnabled default. Disabled by default. */
+#define DOS_CONN_ENABLED_DEFAULT 0
+/* DoSConnectionMaxConcurrentCount default. */
+#define DOS_CONN_MAX_CONCURRENT_COUNT_DEFAULT 100
+/* DoSConnectionDefenseType maps to the dos_conn_defense_type_t enum. */
+#define DOS_CONN_DEFENSE_TYPE_DEFAULT DOS_CONN_DEFENSE_CLOSE
+
+/* Type of defense that we can use for the concurrent connection DoS
+ * mitigation. */
+typedef enum dos_conn_defense_type_t {
+ /* No defense used. */
+ DOS_CONN_DEFENSE_NONE = 1,
+ /* Close immediately the connection meaning refuse it. */
+ DOS_CONN_DEFENSE_CLOSE = 2,
+
+ /* Maximum value that can be used. Useful for the boundaries of the
+ * consensus parameter. */
+ DOS_CONN_DEFENSE_MAX = 2,
+} dos_conn_defense_type_t;
+
+#ifdef DOS_PRIVATE
+
+STATIC uint32_t get_param_conn_max_concurrent_count(
+ const networkstatus_t *ns);
+STATIC uint32_t get_param_cc_circuit_burst(const networkstatus_t *ns);
+STATIC uint32_t get_param_cc_min_concurrent_connection(
+ const networkstatus_t *ns);
+
+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));
+
+#endif /* TOR_DOS_PRIVATE */
+
+#endif /* TOR_DOS_H */
+
diff --git a/src/or/include.am b/src/or/include.am
index ae493b7225..5108a08e53 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -43,6 +43,7 @@ LIBTOR_A_SOURCES = \
src/or/dirvote.c \
src/or/dns.c \
src/or/dnsserv.c \
+ src/or/dos.c \
src/or/fp_pair.c \
src/or/geoip.c \
src/or/entrynodes.c \
@@ -151,6 +152,7 @@ ORHEADERS = \
src/or/dns.h \
src/or/dns_structs.h \
src/or/dnsserv.h \
+ src/or/dos.h \
src/or/ext_orport.h \
src/or/fallback_dirs.inc \
src/or/fp_pair.h \
diff --git a/src/or/main.c b/src/or/main.c
index 187b255bfb..fcd8dc9024 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -34,6 +34,7 @@
#include "dirvote.h"
#include "dns.h"
#include "dnsserv.h"
+#include "dos.h"
#include "entrynodes.h"
#include "geoip.h"
#include "hibernate.h"
@@ -2989,6 +2990,7 @@ tor_free_all(int postfork)
control_free_all();
sandbox_free_getaddrinfo_cache();
protover_free_all();
+ dos_free_all();
if (!postfork) {
config_free_all();
or_state_free_all();
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 991cf80121..d9ae32560e 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -23,6 +23,7 @@
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
+#include "dos.h"
#include "entrynodes.h"
#include "main.h"
#include "microdesc.h"
@@ -1502,6 +1503,15 @@ notify_control_networkstatus_changed(const networkstatus_t *old_c,
smartlist_free(changed);
}
+/* Called when the consensus has changed from old_c to new_c. */
+static void
+notify_networkstatus_changed(const networkstatus_t *old_c,
+ const networkstatus_t *new_c)
+{
+ notify_control_networkstatus_changed(old_c, new_c);
+ dos_consensus_has_changed(new_c);
+}
+
/** Copy all the ancillary information (like router download status and so on)
* from <b>old_c</b> to <b>new_c</b>. */
static void
@@ -1826,8 +1836,7 @@ networkstatus_set_current_consensus(const char *consensus,
const int is_usable_flavor = flav == usable_consensus_flavor();
if (is_usable_flavor) {
- notify_control_networkstatus_changed(
- networkstatus_get_latest_consensus(), c);
+ notify_networkstatus_changed(networkstatus_get_latest_consensus(), c);
}
if (flav == FLAV_NS) {
if (current_ns_consensus) {
diff --git a/src/or/or.h b/src/or/or.h
index 75a02a531e..2cf9e97356 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -4510,6 +4510,36 @@ typedef struct {
/** If 1, we skip all OOS checks. */
int DisableOOSCheck;
+
+ /** Autobool: Is the circuit creation DoS mitigation subsystem enabled? */
+ int DoSCircuitCreationEnabled;
+ /** Minimum concurrent connection needed from one single address before any
+ * defense is used. */
+ int DoSCircuitCreationMinConnections;
+ /** Circuit rate, in tenths of a second, that is used to refill the token
+ * bucket at this given rate. */
+ int DoSCircuitCreationRateTenths;
+ /** Maximum allowed burst of circuits. Reaching that value, the address is
+ * detected as malicious and a defense might be used. */
+ int DoSCircuitCreationBurst;
+ /** When an address is marked as malicous, what defense should be used
+ * against it. See the dos_cc_defense_type_t enum. */
+ int DoSCircuitCreationDefenseType;
+ /** For how much time (in seconds) the 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 value. */
+ int DoSCircuitCreationDefenseTimePeriod;
+
+ /** Autobool: Is the DoS connection mitigation subsystem enabled? */
+ int DoSConnectionEnabled;
+ /** Maximum concurrent connection allowed per address. */
+ int DoSConnectionMaxConcurrentCount;
+ /** When an address is reaches the maximum count, what defense should be
+ * used against it. See the dos_conn_defense_type_t enum. */
+ int DoSConnectionDefenseType;
+
+ /** Autobool: Do we refuse single hop client rendezvous? */
+ int DoSRefuseSingleHopClientRendezvous;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */