aboutsummaryrefslogtreecommitdiff
path: root/src/or
diff options
context:
space:
mode:
Diffstat (limited to 'src/or')
-rw-r--r--src/or/buffers.c3
-rw-r--r--src/or/circuitbuild.c2
-rw-r--r--src/or/circuitbuild.h1
-rw-r--r--src/or/circuitlist.c138
-rw-r--r--src/or/circuitlist.h1
-rw-r--r--src/or/circuituse.c6
-rw-r--r--src/or/command.c14
-rw-r--r--src/or/config.c13
-rw-r--r--src/or/connection.c17
-rw-r--r--src/or/connection_edge.c11
-rw-r--r--src/or/connection_or.c9
-rw-r--r--src/or/microdesc.c2
-rw-r--r--src/or/networkstatus.c13
-rw-r--r--src/or/or.h24
-rw-r--r--src/or/relay.c69
-rw-r--r--src/or/relay.h1
-rw-r--r--src/or/rendcommon.c28
-rw-r--r--src/or/router.c2
-rw-r--r--src/or/routerlist.c31
-rw-r--r--src/or/routerlist.h1
-rw-r--r--src/or/routerparse.c10
21 files changed, 371 insertions, 25 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c
index ad5ab83e4f..9be0476f64 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -147,7 +147,8 @@ static INLINE chunk_freelist_t *
get_freelist(size_t alloc)
{
int i;
- for (i=0; freelists[i].alloc_size <= alloc; ++i) {
+ for (i=0; (freelists[i].alloc_size <= alloc &&
+ freelists[i].alloc_size); ++i ) {
if (freelists[i].alloc_size == alloc) {
return &freelists[i];
}
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index f8521c5cff..1c692ab87b 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -147,7 +147,7 @@ static void pathbias_count_success(origin_circuit_t *circ);
* 3. If we are a directory authority
* 4. If we fail to write circuit build time history to our state file.
*/
-static int
+int
circuit_build_times_disabled(void)
{
if (unit_tests) {
diff --git a/src/or/circuitbuild.h b/src/or/circuitbuild.h
index 984d04a99e..fc6ca65fca 100644
--- a/src/or/circuitbuild.h
+++ b/src/or/circuitbuild.h
@@ -32,6 +32,7 @@ char *circuit_list_path_for_controller(origin_circuit_t *circ);
void circuit_log_path(int severity, unsigned int domain,
origin_circuit_t *circ);
void circuit_rep_hist_note_result(origin_circuit_t *circ);
+int circuit_build_times_disabled(void);
origin_circuit_t *origin_circuit_init(uint8_t purpose, int flags);
origin_circuit_t *circuit_establish_circuit(uint8_t purpose,
extend_info_t *exit,
diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c
index 93ba69dcf0..6250c11d2e 100644
--- a/src/or/circuitlist.c
+++ b/src/or/circuitlist.c
@@ -1359,6 +1359,144 @@ _circuit_mark_for_close(circuit_t *circ, int reason, int line,
}
}
+/** Given a marked circuit <b>circ</b>, aggressively free its cell queues to
+ * recover memory. */
+static void
+marked_circuit_free_cells(circuit_t *circ)
+{
+ if (!circ->marked_for_close) {
+ log_warn(LD_BUG, "Called on non-marked circuit");
+ return;
+ }
+ cell_queue_clear(&circ->n_conn_cells);
+ if (! CIRCUIT_IS_ORIGIN(circ))
+ cell_queue_clear(& TO_OR_CIRCUIT(circ)->p_conn_cells);
+}
+
+/** Return the number of cells used by the circuit <b>c</b>'s cell queues. */
+static size_t
+n_cells_in_circ_queues(const circuit_t *c)
+{
+ size_t n = c->n_conn_cells.n;
+ if (! CIRCUIT_IS_ORIGIN(c))
+ n += TO_OR_CIRCUIT((circuit_t*)c)->p_conn_cells.n;
+ return n;
+}
+
+/**
+ * Return the age of the oldest cell queued on <b>c</b>, in milliseconds.
+ * Return 0 if there are no cells queued on c. Requires that <b>now</b> be
+ * the current time in milliseconds since the epoch, truncated.
+ *
+ * This function will return incorrect results if the oldest cell queued on
+ * the circuit is older than 2**32 msec (about 49 days) old.
+ */
+static uint32_t
+circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
+{
+ uint32_t age = 0;
+ if (c->n_conn_cells.head)
+ age = now - c->n_conn_cells.head->inserted_time;
+
+ if (! CIRCUIT_IS_ORIGIN(c)) {
+ const or_circuit_t *orcirc = TO_OR_CIRCUIT((circuit_t*)c);
+ if (orcirc->p_conn_cells.head) {
+ uint32_t age2 = now - orcirc->p_conn_cells.head->inserted_time;
+ if (age2 > age)
+ return age2;
+ }
+ }
+ return age;
+}
+
+/** Temporary variable for circuits_compare_by_oldest_queued_cell_ This is a
+ * kludge to work around the fact that qsort doesn't provide a way for
+ * comparison functions to take an extra argument. */
+static uint32_t circcomp_now_tmp;
+
+/** Helper to sort a list of circuit_t by age of oldest cell, in descending
+ * order. Requires that circcomp_now_tmp is set correctly. */
+static int
+circuits_compare_by_oldest_queued_cell_(const void **a_, const void **b_)
+{
+ const circuit_t *a = *a_;
+ const circuit_t *b = *b_;
+ uint32_t age_a = circuit_max_queued_cell_age(a, circcomp_now_tmp);
+ uint32_t age_b = circuit_max_queued_cell_age(b, circcomp_now_tmp);
+
+ if (age_a < age_b)
+ return 1;
+ else if (age_a == age_b)
+ return 0;
+ else
+ return -1;
+}
+
+#define FRACTION_OF_CELLS_TO_RETAIN_ON_OOM 0.90
+
+/** We're out of memory for cells, having allocated <b>current_allocation</b>
+ * bytes' worth. Kill the 'worst' circuits until we're under
+ * FRACTION_OF_CIRCS_TO_RETAIN_ON_OOM of our maximum usage. */
+void
+circuits_handle_oom(size_t current_allocation)
+{
+ /* Let's hope there's enough slack space for this allocation here... */
+ smartlist_t *circlist = smartlist_new();
+ circuit_t *circ;
+ size_t n_cells_removed=0, n_cells_to_remove;
+ int n_circuits_killed=0;
+ struct timeval now;
+ log_notice(LD_GENERAL, "We're low on memory. Killing circuits with "
+ "over-long queues. (This behavior is controlled by "
+ "MaxMemInCellQueues.)");
+
+ {
+ size_t mem_target = (size_t)(get_options()->MaxMemInCellQueues *
+ FRACTION_OF_CELLS_TO_RETAIN_ON_OOM);
+ size_t mem_to_recover;
+ if (current_allocation <= mem_target)
+ return;
+ mem_to_recover = current_allocation - mem_target;
+ n_cells_to_remove = CEIL_DIV(mem_to_recover, packed_cell_mem_cost());
+ }
+
+ /* This algorithm itself assumes that you've got enough memory slack
+ * to actually run it. */
+ for (circ = global_circuitlist; circ; circ = circ->next)
+ smartlist_add(circlist, circ);
+
+ /* Set circcomp_now_tmp so that the sort can work. */
+ tor_gettimeofday_cached(&now);
+ circcomp_now_tmp = (uint32_t)tv_to_msec(&now);
+
+ /* This is O(n log n); there are faster algorithms we could use instead.
+ * Let's hope this doesn't happen enough to be in the critical path. */
+ smartlist_sort(circlist, circuits_compare_by_oldest_queued_cell_);
+
+ /* Okay, now the worst circuits are at the front of the list. Let's mark
+ * them, and reclaim their storage aggressively. */
+ SMARTLIST_FOREACH_BEGIN(circlist, circuit_t *, circ) {
+ size_t n = n_cells_in_circ_queues(circ);
+ if (! circ->marked_for_close) {
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ }
+ marked_circuit_free_cells(circ);
+
+ ++n_circuits_killed;
+ n_cells_removed += n;
+ if (n_cells_removed >= n_cells_to_remove)
+ break;
+ } SMARTLIST_FOREACH_END(circ);
+
+ clean_cell_pool(); /* In case this helps. */
+
+ log_notice(LD_GENERAL, "Removed "U64_FORMAT" bytes by killing %d circuits.",
+ U64_PRINTF_ARG(n_cells_removed * packed_cell_mem_cost()),
+ n_circuits_killed);
+
+ smartlist_free(circlist);
+}
+
/** Verify that cpath layer <b>cp</b> has all of its invariants
* correct. Trigger an assert if anything is invalid.
*/
diff --git a/src/or/circuitlist.h b/src/or/circuitlist.h
index 6e7735476b..44f0c1fe3b 100644
--- a/src/or/circuitlist.h
+++ b/src/or/circuitlist.h
@@ -57,6 +57,7 @@ int circuit_count_pending_on_or_conn(or_connection_t *or_conn);
void assert_cpath_layer_ok(const crypt_path_t *cp);
void assert_circuit_ok(const circuit_t *c);
void circuit_free_all(void);
+void circuits_handle_oom(size_t current_allocation);
#endif
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index 20f124eb4e..7218ecc077 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -746,7 +746,7 @@ circuit_predict_and_launch_new(void)
* want, don't do another -- we want to leave a few slots open so
* we can still build circuits preemptively as needed. */
if (num < MAX_UNUSED_OPEN_CIRCUITS-2 &&
- get_options()->LearnCircuitBuildTimeout &&
+ ! circuit_build_times_disabled() &&
circuit_build_times_needs_circuits_now(&circ_times)) {
flags = CIRCLAUNCH_NEED_CAPACITY;
log_info(LD_CIRC,
@@ -882,7 +882,7 @@ circuit_expire_old_circuits_clientside(void)
tor_gettimeofday(&now);
cutoff = now;
- if (get_options()->LearnCircuitBuildTimeout &&
+ if (! circuit_build_times_disabled() &&
circuit_build_times_needs_circuits(&circ_times)) {
/* Circuits should be shorter lived if we need more of them
* for learning a good build timeout */
@@ -1310,7 +1310,7 @@ circuit_launch_by_extend_info(uint8_t purpose,
circ = circuit_find_to_cannibalize(purpose, extend_info, flags);
if (circ) {
uint8_t old_purpose = circ->_base.purpose;
- struct timeval old_timestamp_created;
+ struct timeval old_timestamp_created = circ->_base.timestamp_created;
log_info(LD_CIRC,"Cannibalizing circ '%s' for purpose %d (%s)",
build_state_get_exit_nickname(circ->build_state), purpose,
diff --git a/src/or/command.c b/src/or/command.c
index 8321e261e0..61e1e13a71 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -755,8 +755,8 @@ command_process_versions_cell(var_cell_t *cell, or_connection_t *conn)
const int send_versions = !started_here;
/* If we want to authenticate, send a CERTS cell */
const int send_certs = !started_here || public_server_mode(get_options());
- /* If we're a relay that got a connection, ask for authentication. */
- const int send_chall = !started_here && public_server_mode(get_options());
+ /* If we're a host that got a connection, ask for authentication. */
+ const int send_chall = !started_here;
/* If our certs cell will authenticate us, we can send a netinfo cell
* right now. */
const int send_netinfo = !started_here;
@@ -941,6 +941,16 @@ command_process_netinfo_cell(cell_t *cell, or_connection_t *conn)
* trustworthy. */
(void)my_apparent_addr;
+ if (! conn->handshake_state->sent_netinfo) {
+ /* If we were prepared to authenticate, but we never got an AUTH_CHALLENGE
+ * cell, then we would not previously have sent a NETINFO cell. Do so
+ * now. */
+ if (connection_or_send_netinfo(conn) < 0) {
+ connection_mark_for_close(TO_CONN(conn));
+ return;
+ }
+ }
+
if (connection_or_set_state_open(conn)<0) {
log_fn(LOG_PROTOCOL_WARN, LD_OR, "Got good NETINFO cell from %s:%d; but "
"was unable to make the OR connection become open.",
diff --git a/src/or/config.c b/src/or/config.c
index 90a5dfbda1..991c6fabed 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -343,6 +343,7 @@ static config_var_t _option_vars[] = {
V(MaxAdvertisedBandwidth, MEMUNIT, "1 GB"),
V(MaxCircuitDirtiness, INTERVAL, "10 minutes"),
V(MaxClientCircuitsPending, UINT, "32"),
+ V(MaxMemInCellQueues, MEMUNIT, "8 GB"),
V(MaxOnionsPending, UINT, "100"),
OBSOLETE("MonthlyAccountingStart"),
V(MyFamily, STRING, NULL),
@@ -960,7 +961,7 @@ add_default_trusted_dir_authorities(dirinfo_type_t type)
"76.73.17.194:9030 F397 038A DC51 3361 35E7 B80B D99C A384 4360 292B",
"gabelmoo orport=443 no-v2 "
"v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
- "212.112.245.170:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
+ "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
"dannenberg orport=443 no-v2 "
"v3ident=585769C78764D58426B8B52B6651A5A71137189A "
"193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
@@ -3668,6 +3669,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
log_warn(LD_CONFIG, "EntryNodes is set, but UseEntryGuards is disabled. "
"EntryNodes will be ignored.");
+ if (options->MaxMemInCellQueues < (500 << 20)) {
+ log_warn(LD_CONFIG, "MaxMemInCellQueues must be at least 500 MB for now. "
+ "Ideally, have it as large as you can afford.");
+ options->MaxMemInCellQueues = (500 << 20);
+ }
+
options->_AllowInvalid = 0;
if (options->AllowInvalidNodes) {
SMARTLIST_FOREACH_BEGIN(options->AllowInvalidNodes, const char *, cp) {
@@ -4042,6 +4049,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
REJECT("If you set UseBridges, you must specify at least one bridge.");
if (options->UseBridges && !options->TunnelDirConns)
REJECT("If you set UseBridges, you must set TunnelDirConns.");
+ if (options->RendConfigLines &&
+ (!options->TunnelDirConns || !options->PreferTunneledDirConns))
+ REJECT("If you are running a hidden service, you must set TunnelDirConns "
+ "and PreferTunneledDirConns");
for (cl = options->Bridges; cl; cl = cl->next) {
if (parse_bridge_line(cl->value, 1)<0)
diff --git a/src/or/connection.c b/src/or/connection.c
index eac9c4f32b..e366e0e77f 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -2825,7 +2825,20 @@ connection_read_to_buf(connection_t *conn, ssize_t *max_to_read,
case TOR_TLS_WANTWRITE:
connection_start_writing(conn);
return 0;
- case TOR_TLS_WANTREAD: /* we're already reading */
+ case TOR_TLS_WANTREAD:
+ if (conn->in_connection_handle_write) {
+ /* We've been invoked from connection_handle_write, because we're
+ * waiting for a TLS renegotiation, the renegotiation started, and
+ * SSL_read returned WANTWRITE. But now SSL_read is saying WANTREAD
+ * again. Stop waiting for write events now, or else we'll
+ * busy-loop until data arrives for us to read. */
+ connection_stop_writing(conn);
+ if (!connection_is_reading(conn))
+ connection_start_reading(conn);
+ }
+ /* we're already reading, one hopes */
+ result = 0;
+ break;
case TOR_TLS_DONE: /* no data read, so nothing to process */
result = 0;
break; /* so we call bucket_decrement below */
@@ -3337,7 +3350,9 @@ connection_handle_write(connection_t *conn, int force)
{
int res;
tor_gettimeofday_cache_clear();
+ conn->in_connection_handle_write = 1;
res = connection_handle_write_impl(conn, force);
+ conn->in_connection_handle_write = 0;
return res;
}
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 9563ca6222..362ad9af95 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -3302,12 +3302,13 @@ connection_exit_connect(edge_connection_t *edge_conn)
conn->state = EXIT_CONN_STATE_OPEN;
if (connection_get_outbuf_len(conn)) {
- /* in case there are any queued data cells */
- log_warn(LD_BUG,"newly connected conn had data waiting!");
-// connection_start_writing(conn);
+ /* in case there are any queued data cells, from e.g. optimistic data */
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT|WRITE_EVENT);
+ } else {
+ IF_HAS_NO_BUFFEREVENT(conn)
+ connection_watch_events(conn, READ_EVENT);
}
- IF_HAS_NO_BUFFEREVENT(conn)
- connection_watch_events(conn, READ_EVENT);
/* also, deliver a 'connected' cell back through the circuit. */
if (connection_edge_is_rendezvous_stream(edge_conn)) {
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index 5eecee0740..fbb7c31e04 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -1975,6 +1975,12 @@ connection_or_send_netinfo(or_connection_t *conn)
tor_assert(conn->handshake_state);
+ if (conn->handshake_state->sent_netinfo) {
+ log_warn(LD_BUG, "Attempted to send an extra netinfo cell on a connection "
+ "where we already sent one.");
+ return 0;
+ }
+
memset(&cell, 0, sizeof(cell_t));
cell.command = CELL_NETINFO;
@@ -2009,6 +2015,7 @@ connection_or_send_netinfo(or_connection_t *conn)
}
conn->handshake_state->digest_sent_data = 0;
+ conn->handshake_state->sent_netinfo = 1;
connection_or_write_cell_to_buf(&cell, conn);
return 0;
@@ -2137,7 +2144,7 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
const tor_cert_t *id_cert=NULL, *link_cert=NULL;
const digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
- if (tor_tls_get_my_certs(0, &link_cert, &id_cert))
+ if (tor_tls_get_my_certs(server, &link_cert, &id_cert))
return -1;
my_digests = tor_cert_get_id_digests(id_cert);
their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert);
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index b4d22c1c62..4acec6ae3d 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -561,7 +561,7 @@ microdesc_free(microdesc_t *md)
}
});
if (found) {
- log_warn(LD_BUG, "microdesc_free() called, but md was still referenced "
+ log_info(LD_BUG, "microdesc_free() called, but md was still referenced "
"%d node(s); held_by_nodes == %u", found, md->held_by_nodes);
} else {
log_warn(LD_BUG, "microdesc_free() called with held_by_nodes set to %u, "
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index 2553a74e50..10cc56231f 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -453,6 +453,17 @@ networkstatus_check_document_signature(const networkstatus_t *consensus,
DIGEST_LEN))
return -1;
+ if (authority_cert_is_blacklisted(cert)) {
+ /* We implement blacklisting for authority signing keys by treating
+ * all their signatures as always bad. That way we don't get into
+ * crazy loops of dropping and re-fetching signatures. */
+ log_warn(LD_DIR, "Ignoring a consensus signature made with deprecated"
+ " signing key %s",
+ hex_str(cert->signing_key_digest, DIGEST_LEN));
+ sig->bad_signature = 1;
+ return 0;
+ }
+
signed_digest_len = crypto_pk_keysize(cert->signing_key);
signed_digest = tor_malloc(signed_digest_len);
if (crypto_pk_public_checksig(cert->signing_key,
@@ -1239,7 +1250,7 @@ update_consensus_networkstatus_downloads(time_t now)
}
if (time_to_download_next_consensus[i] > now)
- return; /* Wait until the current consensus is older. */
+ continue; /* Wait until the current consensus is older. */
resource = networkstatus_get_flavor_name(i);
diff --git a/src/or/or.h b/src/or/or.h
index 51c23d305d..462239190b 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -539,6 +539,8 @@ typedef enum {
#define CIRCUIT_PURPOSE_IS_ESTABLISHED_REND(p) \
((p) == CIRCUIT_PURPOSE_C_REND_JOINED || \
(p) == CIRCUIT_PURPOSE_S_REND_JOINED)
+/** True iff the circuit_t c is actually an or_circuit_t */
+#define CIRCUIT_IS_ORCIRC(c) (((circuit_t *)(c))->magic == OR_CIRCUIT_MAGIC)
/** How many circuits do we want simultaneously in-progress to handle
* a given stream? */
@@ -814,6 +816,13 @@ typedef enum {
/** Amount to increment a stream window when we get a stream SENDME. */
#define STREAMWINDOW_INCREMENT 50
+/** Maximum number of queued cells on a circuit for which we are the
+ * midpoint before we give up and kill it. This must be >= circwindow
+ * to avoid killing innocent circuits, and >= circwindow*2 to give
+ * leaky-pipe a chance for being useful someday.
+ */
+#define ORCIRC_MAX_MIDDLE_CELLS (21*(CIRCWINDOW_START_MAX)/10)
+
/* Cell commands. These values are defined in tor-spec.txt. */
#define CELL_PADDING 0
#define CELL_CREATE 1
@@ -903,8 +912,13 @@ typedef struct var_cell_t {
typedef struct packed_cell_t {
struct packed_cell_t *next; /**< Next cell queued on this circuit. */
char body[CELL_NETWORK_SIZE]; /**< Cell as packed for network. */
+ uint32_t inserted_time; /**< Time (in milliseconds since epoch, with high
+ * bits truncated) when this cell was inserted. */
} packed_cell_t;
+/* XXXX This next structure may be obsoleted by inserted_time in
+ * packed_cell_t */
+
/** Number of cells added to a circuit queue including their insertion
* time on 10 millisecond detail; used for buffer statistics. */
typedef struct insertion_time_elem_t {
@@ -998,6 +1012,9 @@ typedef struct connection_t {
/** Set to 1 when we're inside connection_flushed_some to keep us from
* calling connection_handle_write() recursively. */
unsigned int in_flushed_some:1;
+ /** True if connection_handle_write is currently running on this connection.
+ */
+ unsigned int in_connection_handle_write:1;
/* For linked connections:
*/
@@ -1149,6 +1166,9 @@ typedef struct or_handshake_state_t {
/* True iff we've received valid authentication to some identity. */
unsigned int authenticated : 1;
+ /* True iff we have sent a netinfo cell */
+ unsigned int sent_netinfo : 1;
+
/** True iff we should feed outgoing cells into digest_sent and
* digest_received respectively.
*
@@ -3052,6 +3072,10 @@ typedef struct {
config_line_t *DirPort_lines;
config_line_t *DNSPort_lines; /**< Ports to listen on for DNS requests. */
+ uint64_t MaxMemInCellQueues; /**< If we have more memory than this allocated
+ * for circuit cell queues, run the OOM handler
+ */
+
/** @name port booleans
*
* Derived booleans: True iff there is a non-listener port on an AF_INET or
diff --git a/src/or/relay.c b/src/or/relay.c
index a17c333310..a193ad8431 100644
--- a/src/or/relay.c
+++ b/src/or/relay.c
@@ -1799,7 +1799,7 @@ circuit_consider_sending_sendme(circuit_t *circ, crypt_path_t *layer_hint)
#endif
/** The total number of cells we have allocated from the memory pool. */
-static int total_cells_allocated = 0;
+static size_t total_cells_allocated = 0;
/** A memory pool to allocate packed_cell_t objects. */
static mp_pool_t *cell_pool = NULL;
@@ -1871,7 +1871,7 @@ dump_cell_pool_usage(int severity)
++n_circs;
}
log(severity, LD_MM, "%d cells allocated on %d circuits. %d cells leaked.",
- n_cells, n_circs, total_cells_allocated - n_cells);
+ n_cells, n_circs, (int)total_cells_allocated - n_cells);
mp_pool_log_status(cell_pool, severity);
}
@@ -1904,15 +1904,19 @@ cell_queue_append(cell_queue_t *queue, packed_cell_t *cell)
void
cell_queue_append_packed_copy(cell_queue_t *queue, const cell_t *cell)
{
+ struct timeval now;
packed_cell_t *copy = packed_cell_copy(cell);
+ tor_gettimeofday_cached(&now);
+ copy->inserted_time = (uint32_t)tv_to_msec(&now);
+
/* Remember the time when this cell was put in the queue. */
+ /*XXXX This may be obsoleted by inserted_time */
if (get_options()->CellStatistics) {
- struct timeval now;
uint32_t added;
insertion_time_queue_t *it_queue = queue->insertion_times;
if (!it_pool)
it_pool = mp_pool_new(sizeof(insertion_time_elem_t), 1024);
- tor_gettimeofday_cached(&now);
+
#define SECONDS_IN_A_DAY 86400L
added = (uint32_t)(((now.tv_sec % SECONDS_IN_A_DAY) * 100L)
+ ((uint32_t)now.tv_usec / (uint32_t)10000L));
@@ -1978,6 +1982,29 @@ cell_queue_pop(cell_queue_t *queue)
return cell;
}
+/** Return the total number of bytes used for each packed_cell in a queue.
+ * Approximate. */
+size_t
+packed_cell_mem_cost(void)
+{
+ return sizeof(packed_cell_t) + MP_POOL_ITEM_OVERHEAD +
+ get_options()->CellStatistics ?
+ (sizeof(insertion_time_elem_t)+MP_POOL_ITEM_OVERHEAD) : 0;
+}
+
+/** Check whether we've got too much space used for cells. If so,
+ * call the OOM handler and return 1. Otherwise, return 0. */
+static int
+cell_queues_check_size(void)
+{
+ size_t alloc = total_cells_allocated * packed_cell_mem_cost();
+ if (alloc >= get_options()->MaxMemInCellQueues) {
+ circuits_handle_oom(alloc);
+ return 1;
+ }
+ return 0;
+}
+
/** Return a pointer to the "next_active_on_{n,p}_conn" pointer of <b>circ</b>,
* depending on whether <b>conn</b> matches n_conn or p_conn. */
static INLINE circuit_t **
@@ -2532,8 +2559,10 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn,
cell_t *cell, cell_direction_t direction,
streamid_t fromstream)
{
+ or_circuit_t *orcirc = NULL;
cell_queue_t *queue;
int streams_blocked;
+
if (circ->marked_for_close)
return;
@@ -2541,13 +2570,43 @@ append_cell_to_circuit_queue(circuit_t *circ, or_connection_t *orconn,
queue = &circ->n_conn_cells;
streams_blocked = circ->streams_blocked_on_n_conn;
} else {
- or_circuit_t *orcirc = TO_OR_CIRCUIT(circ);
+ orcirc = TO_OR_CIRCUIT(circ);
queue = &orcirc->p_conn_cells;
streams_blocked = circ->streams_blocked_on_p_conn;
}
+ /*
+ * Disabling this for now because of a possible guard discovery attack
+ */
+#if 0
+ /* Are we a middle circuit about to exceed ORCIRC_MAX_MIDDLE_CELLS? */
+ if ((circ->n_conn != NULL) && CIRCUIT_IS_ORCIRC(circ)) {
+ orcirc = TO_OR_CIRCUIT(circ);
+ if (orcirc->p_conn) {
+ if (queue->n + 1 >= ORCIRC_MAX_MIDDLE_CELLS) {
+ /* Queueing this cell would put queue over the cap */
+ log_warn(LD_CIRC,
+ "Got a cell exceeding the cap of %u in the %s direction "
+ "on middle circ ID %u; killing the circuit.",
+ ORCIRC_MAX_MIDDLE_CELLS,
+ (direction == CELL_DIRECTION_OUT) ? "n" : "p",
+ (direction == CELL_DIRECTION_OUT) ?
+ circ->n_circ_id : orcirc->p_circ_id);
+ circuit_mark_for_close(circ, END_CIRC_REASON_RESOURCELIMIT);
+ return;
+ }
+ }
+ }
+#endif
+
cell_queue_append_packed_copy(queue, cell);
+ if (PREDICT_UNLIKELY(cell_queues_check_size())) {
+ /* We ran the OOM handler */
+ if (circ->marked_for_close)
+ return;
+ }
+
/* If we have too many cells on the circuit, we should stop reading from
* the edge streams for a while. */
if (!streams_blocked && queue->n >= CELL_QUEUE_HIGHWATER_SIZE)
diff --git a/src/or/relay.h b/src/or/relay.h
index 41675e2106..c55813b33c 100644
--- a/src/or/relay.h
+++ b/src/or/relay.h
@@ -40,6 +40,7 @@ void init_cell_pool(void);
void free_cell_pool(void);
void clean_cell_pool(void);
void dump_cell_pool_usage(int severity);
+size_t packed_cell_mem_cost(void);
void cell_queue_clear(cell_queue_t *queue);
void cell_queue_append(cell_queue_t *queue, packed_cell_t *cell);
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 4722690c15..76786e0fd1 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -1002,6 +1002,10 @@ rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const char **desc)
return 0;
}
+/* Do not allow more than this many introduction points in a hidden service
+ * descriptor */
+#define MAX_INTRO_POINTS 10
+
/** Parse *desc, calculate its service id, and store it in the cache.
* If we have a newer v0 descriptor with the same ID, ignore this one.
* If we have an older descriptor with the same ID, replace it.
@@ -1070,6 +1074,16 @@ rend_cache_store(const char *desc, size_t desc_len, int published,
rend_service_descriptor_free(parsed);
return -1;
}
+ if (parsed->intro_nodes &&
+ smartlist_len(parsed->intro_nodes) > MAX_INTRO_POINTS) {
+ log_warn(LD_REND, "Found too many introduction points on a hidden "
+ "service descriptor for %s. This is probably a (misguided) "
+ "attempt to improve reliability, but it could also be an "
+ "attempt to do a guard enumeration attack. Rejecting.",
+ safe_str_client(query));
+ rend_service_descriptor_free(parsed);
+ return -2;
+ }
tor_snprintf(key, sizeof(key), "0%s", query);
e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
if (e && e->parsed->timestamp > parsed->timestamp) {
@@ -1288,6 +1302,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
}
/* Decode/decrypt introduction points. */
if (intro_content) {
+ int n_intro_points;
if (rend_query->auth_type != REND_NO_AUTH &&
!tor_mem_is_zero(rend_query->descriptor_cookie,
sizeof(rend_query->descriptor_cookie))) {
@@ -1308,13 +1323,22 @@ rend_cache_store_v2_desc_as_client(const char *desc,
intro_size = ipos_decrypted_size;
}
}
- if (rend_parse_introduction_points(parsed, intro_content,
- intro_size) <= 0) {
+ n_intro_points = rend_parse_introduction_points(parsed, intro_content,
+ intro_size);
+ if (n_intro_points <= 0) {
log_warn(LD_REND, "Failed to parse introduction points. Either the "
"service has published a corrupt descriptor or you have "
"provided invalid authorization data.");
retval = -2;
goto err;
+ } else if (n_intro_points > MAX_INTRO_POINTS) {
+ log_warn(LD_REND, "Found too many introduction points on a hidden "
+ "service descriptor for %s. This is probably a (misguided) "
+ "attempt to improve reliability, but it could also be an "
+ "attempt to do a guard enumeration attack. Rejecting.",
+ safe_str_client(rend_query->onion_address));
+ retval = -2;
+ goto err;
}
} else {
log_info(LD_REND, "Descriptor does not contain any introduction points.");
diff --git a/src/or/router.c b/src/or/router.c
index 1ace8e2492..a3459aec71 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -177,7 +177,7 @@ get_server_identity_key(void)
int
server_identity_key_is_set(void)
{
- return server_identitykey != NULL;
+ return server_mode(get_options()) && server_identitykey != NULL;
}
/** Set the current client identity key to <b>k</b>.
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 3c39e362df..9e59c332a1 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -458,6 +458,37 @@ authority_cert_dl_failed(const char *id_digest, int status)
download_status_failed(&cl->dl_status, status);
}
+static const char *BAD_SIGNING_KEYS[] = {
+ "09CD84F751FD6E955E0F8ADB497D5401470D697E", // Expires 2015-01-11 16:26:31
+ "0E7E9C07F0969D0468AD741E172A6109DC289F3C", // Expires 2014-08-12 10:18:26
+ "57B85409891D3FB32137F642FDEDF8B7F8CDFDCD", // Expires 2015-02-11 17:19:09
+ "87326329007AF781F587AF5B594E540B2B6C7630", // Expires 2014-07-17 11:10:09
+ "98CC82342DE8D298CF99D3F1A396475901E0D38E", // Expires 2014-11-10 13:18:56
+ "9904B52336713A5ADCB13E4FB14DC919E0D45571", // Expires 2014-04-20 20:01:01
+ "9DCD8E3F1DD1597E2AD476BBA28A1A89F3095227", // Expires 2015-01-16 03:52:30
+ "A61682F34B9BB9694AC98491FE1ABBFE61923941", // Expires 2014-06-11 09:25:09
+ "B59F6E99C575113650C99F1C425BA7B20A8C071D", // Expires 2014-07-31 13:22:10
+ "D27178388FA75B96D37FA36E0B015227DDDBDA51", // Expires 2014-08-04 04:01:57
+ NULL,
+};
+
+/** DOCDOC */
+int
+authority_cert_is_blacklisted(const authority_cert_t *cert)
+{
+ char hex_digest[HEX_DIGEST_LEN+1];
+ int i;
+ base16_encode(hex_digest, sizeof(hex_digest),
+ cert->signing_key_digest, sizeof(cert->signing_key_digest));
+
+ for (i = 0; BAD_SIGNING_KEYS[i]; ++i) {
+ if (!strcasecmp(hex_digest, BAD_SIGNING_KEYS[i])) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
/** Return true iff when we've been getting enough failures when trying to
* download the certificate with ID digest <b>id_digest</b> that we're willing
* to start bugging the user about it. */
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 8dcc6eb026..bd55b7b201 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -25,6 +25,7 @@ void authority_cert_dl_failed(const char *id_digest, int status);
void authority_certs_fetch_missing(networkstatus_t *status, time_t now);
int router_reload_router_list(void);
int authority_cert_dl_looks_uncertain(const char *id_digest);
+int authority_cert_is_blacklisted(const authority_cert_t *cert);
smartlist_t *router_get_trusted_dir_servers(void);
const routerstatus_t *router_pick_directory_server(dirinfo_type_t type,
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 299d07d376..3ff887c3ca 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -3053,6 +3053,14 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
goto err;
}
+ if (ns->type != NS_TYPE_CONSENSUS) {
+ if (authority_cert_is_blacklisted(ns->cert)) {
+ log_warn(LD_DIR, "Rejecting vote signature made with blacklisted "
+ "signing key %s",
+ hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
+ goto err;
+ }
+ }
voter->address = tor_strdup(tok->args[2]);
if (!tor_inet_aton(tok->args[3], &in)) {
log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
@@ -4447,11 +4455,13 @@ microdescs_parse_from_string(const char *s, const char *eos,
microdesc_free(md);
md = NULL;
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
memarea_clear(area);
smartlist_clear(tokens);
s = start_of_next_microdesc;
}
+ SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
memarea_drop_all(area);
smartlist_free(tokens);