diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/buffers.c | 3 | ||||
-rw-r--r-- | src/or/circuitbuild.c | 2 | ||||
-rw-r--r-- | src/or/circuitbuild.h | 1 | ||||
-rw-r--r-- | src/or/circuitlist.c | 138 | ||||
-rw-r--r-- | src/or/circuitlist.h | 1 | ||||
-rw-r--r-- | src/or/circuituse.c | 6 | ||||
-rw-r--r-- | src/or/command.c | 14 | ||||
-rw-r--r-- | src/or/config.c | 13 | ||||
-rw-r--r-- | src/or/connection.c | 17 | ||||
-rw-r--r-- | src/or/connection_edge.c | 11 | ||||
-rw-r--r-- | src/or/connection_or.c | 9 | ||||
-rw-r--r-- | src/or/microdesc.c | 2 | ||||
-rw-r--r-- | src/or/networkstatus.c | 13 | ||||
-rw-r--r-- | src/or/or.h | 24 | ||||
-rw-r--r-- | src/or/relay.c | 69 | ||||
-rw-r--r-- | src/or/relay.h | 1 | ||||
-rw-r--r-- | src/or/rendcommon.c | 28 | ||||
-rw-r--r-- | src/or/router.c | 2 | ||||
-rw-r--r-- | src/or/routerlist.c | 31 | ||||
-rw-r--r-- | src/or/routerlist.h | 1 | ||||
-rw-r--r-- | src/or/routerparse.c | 10 |
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); |