[tor-bugs] #20511 [Core Tor/Tor]: add a failsafe where if you're about to serve a consensus that you know is obsolete, don't do it

Tor Bug Tracker & Wiki blackhole at torproject.org
Sat Nov 5 16:18:43 UTC 2016


#20511: add a failsafe where if you're about to serve a consensus that you know is
obsolete, don't do it
--------------------------+------------------------------------
 Reporter:  arma          |          Owner:
     Type:  enhancement   |         Status:  new
 Priority:  Medium        |      Milestone:  Tor: 0.3.0.x-final
Component:  Core Tor/Tor  |        Version:
 Severity:  Normal        |     Resolution:
 Keywords:                |  Actual Points:
Parent ID:                |         Points:
 Reviewer:                |        Sponsor:
--------------------------+------------------------------------

Comment (by rubiate):

 Will this work? I'm not sure that this is all that's needed or if there's
 other places that it needs to be checked.

 I thought Tor should warn that this has happened, but probably shouldn't
 do it at a normal log-level on every dir request as that could get noisy.

 {{{
 diff --git a/src/or/directory.c b/src/or/directory.c
 index ba6d38c..16e33c5 100644
 --- a/src/or/directory.c
 +++ b/src/or/directory.c
 @@ -2939,6 +2939,32 @@ handle_get_frontpage(dir_connection_t *conn, const
 get_handler_args_t *args)
    return 0;
  }

 +/** Warn that the consensus <b>v</b> of type <b>flavor</b> is too old and
 will
 + * not be served. Log the message at a lower severity if we have already
 + * warned about this recently.
 + */
 +static void
 +warn_consensus_is_too_old(networkstatus_t *v, const char *flavor, time_t
 now)
 +{
 +  static time_t warned = 0;
 +  int severity;
 +  char timestamp[ISO_TIME_LEN+1];
 +
 +  if (now >= warned + 60 * 60) {
 +    severity = LOG_WARN;
 +    warned = now;
 +  } else {
 +    severity = LOG_DEBUG;
 +  }
 +
 +  format_local_iso_time(timestamp, v->valid_until);
 +  log_fn(severity, LD_DIRSERV,
 +         "Our %s%sconsensus is too old, we will not serve it to clients.
 "
 +         "It was valid until %s and we continued to serve it for up to 24
 "
 +         "hours after it expired.",
 +         flavor ? flavor : "", flavor ? " " : "", timestamp);
 +}
 +
  /** Helper function for GET /tor/status-vote/current/consensus
   */
  static int
 @@ -2983,6 +3009,15 @@ handle_get_current_consensus(dir_connection_t
 *conn,

        v = networkstatus_get_latest_consensus_by_flavor(flav);

 +      if (v && !networkstatus_consensus_reasonably_live(v, now)) {
 +        write_http_status_line(conn, 404, "Consensus is too old");
 +        warn_consensus_is_too_old(v, flavor, now);
 +        smartlist_free(dir_fps);
 +        geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
 +        tor_free(flavor);
 +        goto done;
 +      }
 +
        if (v && want_fps &&
            !client_likes_consensus(v, want_fps)) {
          write_http_status_line(conn, 404, "Consensus not signed by
 sufficient "
 diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
 index ed888fb..7f200b9 100644
 --- a/src/or/networkstatus.c
 +++ b/src/or/networkstatus.c
 @@ -1342,6 +1342,21 @@ networkstatus_get_live_consensus,(time_t now))
      return NULL;
  }

 +/** Determine if <b>consensus</b> is valid or expired recently enough
 that
 + * we can still use it.
 + *
 + * Return 1 if the consensus is reasonably live, or 0 if it is too old.
 + */
 +int
 +networkstatus_consensus_reasonably_live(networkstatus_t *consensus,
 time_t now)
 +{
 +#define REASONABLY_LIVE_TIME (24*60*60)
 +  if (now <= consensus->valid_until + REASONABLY_LIVE_TIME)
 +    return 1;
 +
 +  return 0;
 +}
 +
  /* XXXX remove this in favor of get_live_consensus. But actually,
   * leave something like it for bridge users, who need to not totally
   * lose if they spend a while fetching a new consensus. */
 @@ -1350,12 +1365,11 @@ networkstatus_get_live_consensus,(time_t now))
  networkstatus_t *
  networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
  {
 -#define REASONABLY_LIVE_TIME (24*60*60)
    networkstatus_t *consensus =
      networkstatus_get_latest_consensus_by_flavor(flavor);
    if (consensus &&
        consensus->valid_after <= now &&
 -      now <= consensus->valid_until+REASONABLY_LIVE_TIME)
 +      networkstatus_consensus_reasonably_live(consensus, now))
      return consensus;
    else
      return NULL;
 diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
 index 71f36b6..172c0ea 100644
 --- a/src/or/networkstatus.h
 +++ b/src/or/networkstatus.h
 @@ -79,6 +79,8 @@ MOCK_DECL(networkstatus_t
 *,networkstatus_get_latest_consensus,(void));
  MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
            (consensus_flavor_t f));
  MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t
 now));
 +int networkstatus_consensus_reasonably_live(networkstatus_t *consensus,
 +                                            time_t now);
  networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
                                                               int flavor);
  MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
 }}}

 Tests and changes file:

 Note there's an existing test that needed to be changed to set the
 valid_until time or else it fails for it's consensus being too old.

 {{{
 diff --git a/changes/20511 b/changes/20511
 new file mode 100644
 index 0000000..d6e962e
 --- /dev/null
 +++ b/changes/20511
 @@ -0,0 +1,3 @@
 +  o Minor feature:
 +    - Relays and bridges will now refuse to serve the consensus they have
 if
 +      they know it is too old for a client to use. Closes ticket 20511.
 diff --git a/src/test/test_dir_handle_get.c
 b/src/test/test_dir_handle_get.c
 index c215fee..cab3fb2 100644
 --- a/src/test/test_dir_handle_get.c
 +++ b/src/test/test_dir_handle_get.c
 @@ -30,6 +30,7 @@
  #include "dirserv.h"
  #include "torgzip.h"
  #include "dirvote.h"
 +#include "log_test_helpers.h"

  #ifdef _WIN32
  /* For mkdir() */
 @@ -53,6 +54,7 @@ ENABLE_GCC_WARNING(overlength-strings)
  #define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
  #define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
  #define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again
 later\r\n\r\n"
 +#define TOO_OLD "HTTP/1.0 404 Consensus is too old\r\n\r\n"
  #define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
    "Consensus not signed by sufficient number of requested
 authorities\r\n\r\n"

 @@ -1617,6 +1619,7 @@
 test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void*
 d)
    mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
    mock_ns_val->flavor = FLAV_NS;
    mock_ns_val->voters = smartlist_new();
 +  mock_ns_val->valid_until = time(NULL);

    /* init mock */
    init_mock_options();
 @@ -1696,6 +1699,63 @@
 test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
      or_options_free(mock_options); mock_options = NULL;
  }

 +static void
 +test_dir_handle_get_status_vote_current_consensus_too_old(void *data)
 +{
 +  dir_connection_t *conn = NULL;
 +  char *header = NULL;
 +  (void)data;
 +
 +  mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
 +  mock_ns_val->flavor = FLAV_MICRODESC;
 +  mock_ns_val->valid_until = time(NULL) - (60 * 60 * 24) - 1;
 +
 +  init_mock_options();
 +  MOCK(get_options, mock_get_options);
 +  MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
 +  MOCK(networkstatus_get_latest_consensus_by_flavor,
 mock_ns_get_by_flavor);
 +
 +  conn = new_dir_conn();
 +
 +  setup_capture_of_logs(LOG_WARN);
 +
 +  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
 +    GET("/tor/status-vote/current/consensus-microdesc"), NULL, 0));
 +
 +  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
 +                      NULL, NULL, 1, 0);
 +  tt_assert(header);
 +  tt_str_op(TOO_OLD, OP_EQ, header);
 +
 +  expect_log_msg_containing("too old");
 +
 +  tor_free(header);
 +  header = NULL;
 +  teardown_capture_of_logs();
 +
 +  setup_capture_of_logs(LOG_WARN);
 +
 +  tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
 +    GET("/tor/status-vote/current/consensus"), NULL, 0));
 +
 +  fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
 +                      NULL, NULL, 1, 0);
 +  tt_assert(header);
 +  tt_str_op(TOO_OLD, OP_EQ, header);
 +
 +  expect_no_log_entry();
 +
 +  done:
 +    teardown_capture_of_logs();
 +    UNMOCK(networkstatus_get_latest_consensus_by_flavor);
 +    UNMOCK(connection_write_to_buf_impl_);
 +    UNMOCK(get_options);
 +    connection_free_(TO_CONN(conn));
 +    tor_free(header);
 +    tor_free(mock_ns_val);
 +    or_options_free(mock_options); mock_options = NULL;
 +}
 +
  NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));

  int
 @@ -2481,6 +2541,7 @@ struct testcase_t dir_handle_get_tests[] = {
    DIR_HANDLE_CMD(status_vote_next_authority, 0),
    DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
    DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
 +  DIR_HANDLE_CMD(status_vote_current_consensus_too_old, 0),
    DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
    DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
    DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),


 }}}

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/20511#comment:4>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list