Commit ec8a06c5 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Require a threshold of exit nodes before building circuits

This mitigates an attack proposed by wanoskarnet, in which all of a
client's bridges collude to restrict the exit nodes that the client
knows about.  Fixes bug 5343.
parent fe50b676
Loading
Loading
Loading
Loading

changes/bug5343

0 → 100644
+7 −0
Original line number Diff line number Diff line
  o Security fixes:
    - Only build circuits if we have a sufficient threshold of the total
      descriptors marked in the consensus with the "Exit" flag.  This
      mitigates an attack proposed by wanoskarnet, in which all of a
      client's bridges collude to restrict the exit nodes that the
      client knows about.  Fixes bug 5343.
+24 −5
Original line number Diff line number Diff line
@@ -4788,18 +4788,21 @@ get_dir_info_status_string(void)
 * them seem like ones we'd use, and how many of <em>those</em> we have
 * descriptors for.  Store the former in *<b>num_usable</b> and the latter in
 * *<b>num_present</b>.  If <b>in_set</b> is non-NULL, only consider those
 * routers in <b>in_set</b>.
 * routers in <b>in_set</b>.  If <b>exit_only</b> is true, only consider nodes
 * with the Exit flag.
 */
static void
count_usable_descriptors(int *num_present, int *num_usable,
                         const networkstatus_t *consensus,
                         or_options_t *options, time_t now,
                         routerset_t *in_set)
                         routerset_t *in_set, int exit_only)
{
  *num_present = 0, *num_usable=0;

  SMARTLIST_FOREACH(consensus->routerstatus_list, routerstatus_t *, rs,
     {
       if (exit_only && ! rs->is_exit)
         continue;
       if (in_set && ! routerset_contains_routerstatus(in_set, rs))
         continue;
       if (client_would_use_router(rs, now, options)) {
@@ -4830,7 +4833,7 @@ count_loading_descriptors_progress(void)
    return 0; /* can't count descriptors if we have no list of them */

  count_usable_descriptors(&num_present, &num_usable,
                           consensus, get_options(), now, NULL);
                           consensus, get_options(), now, NULL, 0);

  if (num_usable == 0)
    return 0; /* don't div by 0 */
@@ -4849,6 +4852,7 @@ static void
update_router_have_minimum_dir_info(void)
{
  int num_present = 0, num_usable=0;
  int num_exit_present = 0, num_exit_usable = 0;
  time_t now = time(NULL);
  int res;
  or_options_t *options = get_options();
@@ -4875,7 +4879,9 @@ update_router_have_minimum_dir_info(void)
  }

  count_usable_descriptors(&num_present, &num_usable, consensus, options, now,
                           NULL);
                           NULL, 0);
  count_usable_descriptors(&num_exit_present, &num_exit_usable,
                           consensus, options, now, options->ExitNodes, 1);

  if (num_present < num_usable/4) {
    tor_snprintf(dir_info_status, sizeof(dir_info_status),
@@ -4889,12 +4895,25 @@ update_router_have_minimum_dir_info(void)
                 num_present, num_present ? "" : "s");
    res = 0;
    goto done;
  } else if (num_exit_present < num_exit_usable / 3) {
    tor_snprintf(dir_info_status, sizeof(dir_info_status),
                 "We have only %d/%d usable exit node descriptors.",
                 num_exit_present, num_exit_usable);
    res = 0;
    control_event_bootstrap(BOOTSTRAP_STATUS_REQUESTING_DESCRIPTORS, 0);
    goto done;
  } else if (num_exit_present < 2) {
    tor_snprintf(dir_info_status, sizeof(dir_info_status),
                 "Only %d descriptor%s here and believed reachable!",
                 num_exit_present, num_exit_present ? "" : "s");
    res = 0;
    goto done;
  }

  /* Check for entry nodes. */
  if (options->EntryNodes) {
    count_usable_descriptors(&num_present, &num_usable, consensus, options,
                             now, options->EntryNodes);
                             now, options->EntryNodes, 0);

    if (!num_usable || !num_present) {
      tor_snprintf(dir_info_status, sizeof(dir_info_status),