Skip to content
Snippets Groups Projects
relay_config.c 53.7 KiB
Newer Older

    int valid_line = 1;
    smartlist_t *sl = smartlist_new();
    smartlist_split_string(sl, line, ",",
      SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK|SPLIT_STRIP_SPACE, 0);
    SMARTLIST_FOREACH_BEGIN(sl, char *, s)
    {
      char *normalized = NULL;
      if (!is_legal_nickname_or_hexdigest(s)) {
        // check if first char is dollar
        if (s[0] != '$') {
          // Try again but with a dollar symbol prepended
          char *prepended;
          tor_asprintf(&prepended, "$%s", s);

          if (is_legal_nickname_or_hexdigest(prepended)) {
            // The nickname is valid when it's prepended, set it as the
            // normalized version
            normalized = prepended;
          } else {
            // Still not valid, free and fallback to error message
            tor_free(prepended);
          }
        }

        if (!normalized) {
          tor_asprintf(msg, "Invalid nickname '%s' in %s line", s, name);
          valid_line = 0;
          break;
        }
      } else {
        normalized = tor_strdup(s);
      }

      config_line_t *next = tor_malloc_zero(sizeof(*next));
      next->key = tor_strdup(cl->key);
      next->value = normalized;
      next->next = NULL;

      *new_nicknames_next = next;
      new_nicknames_next = &next->next;
    } SMARTLIST_FOREACH_END(s);

    SMARTLIST_FOREACH(sl, char *, s, tor_free(s));
    smartlist_free(sl);

    if (!valid_line) {
      config_free_lines(new_nicknames);
      return -1;
    }
  }

  *normalized_out = new_nicknames;

  return 0;
}

#define ONE_MEGABYTE (UINT64_C(1) << 20)

/* If we have less than 300 MB suggest disabling dircache */
#define DIRCACHE_MIN_MEM_MB 300
#define DIRCACHE_MIN_MEM_BYTES (DIRCACHE_MIN_MEM_MB*ONE_MEGABYTE)
#define STRINGIFY(val) #val

/** Create a warning message for emitting if we are a dircache but may not have
 * enough system memory, or if we are not a dircache but probably should be.
 * Return -1 when a message is returned in *msg*, else return 0. */
STATIC int
have_enough_mem_for_dircache(const or_options_t *options, size_t total_mem,
                             char **msg)
{
  *msg = NULL;
  /* XXX We should possibly be looking at MaxMemInQueues here
   * unconditionally.  Or we should believe total_mem unconditionally. */
  if (total_mem == 0) {
    if (get_total_system_memory(&total_mem) < 0) {
      total_mem = options->MaxMemInQueues >= SIZE_MAX ?
        SIZE_MAX : (size_t)options->MaxMemInQueues;
    }
  }
  if (options->DirCache) {
    if (total_mem < DIRCACHE_MIN_MEM_BYTES) {
      if (options->BridgeRelay) {
        tor_asprintf(msg, "Running a Bridge with less than %d MB of memory "
                       "is not recommended.", DIRCACHE_MIN_MEM_MB);
      } else {
        tor_asprintf(msg, "Being a directory cache (default) with less than "
                       "%d MB of memory is not recommended and may consume "
                       "most of the available resources. Consider disabling "
                       "this functionality by setting the DirCache option "
                       "to 0.", DIRCACHE_MIN_MEM_MB);
      }
    }
  } else {
    if (total_mem >= DIRCACHE_MIN_MEM_BYTES) {
      *msg = tor_strdup("DirCache is disabled and we are configured as a "
               "relay. We will not become a Guard.");
    }
  }
  return *msg == NULL ? 0 : -1;
}
#undef STRINGIFY

/**
 * Legacy validation/normalization function for the relay mode options.
 * Uses old_options as the previous options.
 *
 * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
 * on error.
 */
int
options_validate_relay_mode(const or_options_t *old_options,
                            or_options_t *options,
                            char **msg)
{
  (void)old_options;

  if (BUG(!options))
    return -1;

  if (BUG(!msg))
    return -1;

  if (server_mode(options) && options->RendConfigLines &&
      !hs_service_non_anonymous_mode_enabled(options))
    log_warn(LD_CONFIG,
        "Tor is currently configured as a relay and a hidden service. "
        "That's not very secure: you should probably run your hidden service "
        "in a separate Tor process, at least -- see "
        "https://bugs.torproject.org/tpo/core/tor/8742.");
  if (options->BridgeRelay && options->DirPort_set) {
    log_warn(LD_CONFIG, "Can't set a DirPort on a bridge relay; disabling "
             "DirPort");
    config_free_lines(options->DirPort_lines);
    options->DirPort_lines = NULL;
    options->DirPort_set = 0;
  }

  if (options->DirPort_set && !options->DirCache) {
    REJECT("DirPort configured but DirCache disabled. DirPort requires "
           "DirCache.");
  }

  if (options->BridgeRelay && !options->DirCache) {
    REJECT("We're a bridge but DirCache is disabled. BridgeRelay requires "
           "DirCache.");
  }

  if (options->BridgeRelay == 1 && ! options->ORPort_set)
    REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
           "combination.");

  if (options->BridgeRelay == 1 && !(options->ExitRelay == 0 ||
      policy_using_default_exit_options(options))) {
    log_warn(LD_CONFIG, "BridgeRelay is 1, but ExitRelay is 1 or an "
           "ExitPolicy is configured. Tor will start, but it will not "
           "function as an exit relay.");
  }

  if (server_mode(options)) {
    char *dircache_msg = NULL;
    if (have_enough_mem_for_dircache(options, 0, &dircache_msg)) {
      log_warn(LD_CONFIG, "%s", dircache_msg);
      tor_free(dircache_msg);
    }
  }

  if (options->MyFamily_lines && options->BridgeRelay) {
    log_warn(LD_CONFIG, "Listing a family for a bridge relay is not "
             "supported: it can reveal bridge fingerprints to censors. "
             "You should also make sure you aren't listing this bridge's "
             "fingerprint in any other MyFamily.");
  }
  if (options->MyFamily_lines && !options->ContactInfo) {
    log_warn(LD_CONFIG, "MyFamily is set but ContactInfo is not configured. "
             "ContactInfo should always be set when MyFamily option is too.");
  }
  if (normalize_nickname_list(&options->MyFamily,
                              options->MyFamily_lines, "MyFamily", msg))
    return -1;

  if (options->ConstrainedSockets) {
    if (options->DirPort_set) {
      /* Providing cached directory entries while system TCP buffers are scarce
       * will exacerbate the socket errors.  Suggest that this be disabled. */
      COMPLAIN("You have requested constrained socket buffers while also "
               "serving directory entries via DirPort.  It is strongly "
               "suggested that you disable serving directory requests when "
               "system TCP buffer resources are scarce.");
    }
  }

  return 0;
}

/**
 * Legacy validation/normalization function for the relay testing options
 * in options. Uses old_options as the previous options.
 *
 * Returns 0 on success, returns -1 and sets *msg to a newly allocated string
 * on error.
 */
int
options_validate_relay_testing(const or_options_t *old_options,
                               or_options_t *options,
                               char **msg)
{
  (void)old_options;

  if (BUG(!options))
    return -1;

  if (BUG(!msg))
    return -1;

  if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
    REJECT("SigningKeyLifetime is too short.");
  if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2)
    REJECT("LinkCertLifetime is too short.");
  if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2)
    REJECT("TestingAuthKeyLifetime is too short.");


/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
 * will require us to rotate the CPU and DNS workers; else return 0. */
static int
options_transition_affects_workers(const or_options_t *old_options,
                                   const or_options_t *new_options)
{
  YES_IF_CHANGED_STRING(DataDirectory);
  YES_IF_CHANGED_INT(NumCPUs);
  YES_IF_CHANGED_LINELIST(ORPort_lines);
  YES_IF_CHANGED_BOOL(ServerDNSSearchDomains);
  YES_IF_CHANGED_BOOL(SafeLogging_);
  YES_IF_CHANGED_BOOL(ClientOnly);
  YES_IF_CHANGED_BOOL(LogMessageDomains);
  YES_IF_CHANGED_LINELIST(Logs);

  if (server_mode(old_options) != server_mode(new_options) ||
      public_server_mode(old_options) != public_server_mode(new_options) ||
      dir_server_mode(old_options) != dir_server_mode(new_options))
    return 1;

  /* Nothing that changed matters. */
  return 0;
}

/** Return 1 if any change from <b>old_options</b> to <b>new_options</b>
 * will require us to generate a new descriptor; else return 0. */
static int
options_transition_affects_descriptor(const or_options_t *old_options,
                                      const or_options_t *new_options)
{
  /* XXX We can be smarter here. If your DirPort isn't being
   * published and you just turned it off, no need to republish. Etc. */

  YES_IF_CHANGED_STRING(DataDirectory);
  YES_IF_CHANGED_STRING(Nickname);
  YES_IF_CHANGED_LINELIST(Address);
  YES_IF_CHANGED_LINELIST(ExitPolicy);
  YES_IF_CHANGED_BOOL(ExitRelay);
  YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate);
  YES_IF_CHANGED_BOOL(ExitPolicyRejectLocalInterfaces);
  YES_IF_CHANGED_BOOL(IPv6Exit);
  YES_IF_CHANGED_LINELIST(ORPort_lines);
  YES_IF_CHANGED_LINELIST(DirPort_lines);
  YES_IF_CHANGED_LINELIST(DirPort_lines);
  YES_IF_CHANGED_BOOL(ClientOnly);
  YES_IF_CHANGED_BOOL(DisableNetwork);
  YES_IF_CHANGED_BOOL(PublishServerDescriptor_);
  YES_IF_CHANGED_STRING(ContactInfo);
  YES_IF_CHANGED_STRING(BridgeDistribution);
  YES_IF_CHANGED_LINELIST(MyFamily);
  YES_IF_CHANGED_STRING(AccountingStart);
  YES_IF_CHANGED_INT(AccountingMax);
  YES_IF_CHANGED_INT(AccountingRule);
  YES_IF_CHANGED_BOOL(DirCache);
  YES_IF_CHANGED_BOOL(AssumeReachable);

  if (relay_get_effective_bwrate(old_options) !=
        relay_get_effective_bwrate(new_options) ||
      relay_get_effective_bwburst(old_options) !=
        relay_get_effective_bwburst(new_options) ||
      public_server_mode(old_options) != public_server_mode(new_options))
    return 1;

  return 0;
}

/** Fetch the active option list, and take relay actions based on it. All of
 * the things we do should survive being done repeatedly.  If present,
 * <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay(const or_options_t *old_options)
{
  const or_options_t *options = get_options();

  const int transition_affects_workers =
    old_options && options_transition_affects_workers(old_options, options);

  /* We want to reinit keys as needed before we do much of anything else:
     keys are important, and other things can depend on them. */
  if (transition_affects_workers ||
      (authdir_mode_v3(options) && (!old_options ||
                                    !authdir_mode_v3(old_options)))) {
    if (init_keys() < 0) {
      log_warn(LD_BUG,"Error initializing keys; exiting");
      return -1;
    }
  }

  if (server_mode(options)) {
    static int cdm_initialized = 0;
    if (cdm_initialized == 0) {
      cdm_initialized = 1;
      consdiffmgr_configure(NULL);
      consdiffmgr_validate();
    }
  }

  /* Check for transitions that need action. */
  if (old_options) {
    if (transition_affects_workers) {
      log_info(LD_GENERAL,
               "Worker-related options changed. Rotating workers.");
      const int server_mode_turned_on =
        server_mode(options) && !server_mode(old_options);

      if (server_mode_turned_on) {
        ip_address_changed(0);
      }
      cpuworkers_rotate_keyinfo();
    }
  }

  return 0;
}

/** Fetch the active option list, and take relay accounting actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_accounting(const or_options_t *old_options)
{
  (void)old_options;

  const or_options_t *options = get_options();

  /* Set up accounting */
  if (accounting_parse_options(options, 0)<0) {
    // LCOV_EXCL_START
    log_warn(LD_BUG,"Error in previously validated accounting options");
    return -1;
    // LCOV_EXCL_STOP
  }
  if (accounting_is_enabled(options))
    configure_accounting(time(NULL));

  return 0;
}

/** Fetch the active option list, and take relay bandwidth actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_bandwidth(const or_options_t *old_options)
{
  const or_options_t *options = get_options();

  /* Check for transitions that need action. */
  if (old_options) {
    if (options->PerConnBWRate != old_options->PerConnBWRate ||
        options->PerConnBWBurst != old_options->PerConnBWBurst)
      connection_or_update_token_buckets(get_connection_array(), options);

    if (options->RelayBandwidthRate != old_options->RelayBandwidthRate ||
        options->RelayBandwidthBurst != old_options->RelayBandwidthBurst)
      connection_bucket_adjust(options);
  }

  return 0;
}

/** Fetch the active option list, and take bridge statistics actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_bridge_stats(const or_options_t *old_options)
{
  const or_options_t *options = get_options();

/* How long should we delay counting bridge stats after becoming a bridge?
 * We use this so we don't count clients who used our bridge thinking it is
 * a relay. If you change this, don't forget to change the log message
 * below. It's 4 hours (the time it takes to stop being used by clients)
 * plus some extra time for clock skew. */
#define RELAY_BRIDGE_STATS_DELAY (6 * 60 * 60)

  /* Check for transitions that need action. */
  if (old_options) {
    if (! bool_eq(options->BridgeRelay, old_options->BridgeRelay)) {
      int was_relay = 0;
      if (options->BridgeRelay) {
        time_t int_start = time(NULL);
        if (config_lines_eq(old_options->ORPort_lines,options->ORPort_lines)) {
          int_start += RELAY_BRIDGE_STATS_DELAY;
          was_relay = 1;
        }
        geoip_bridge_stats_init(int_start);
        log_info(LD_CONFIG, "We are acting as a bridge now.  Starting new "
                 "GeoIP stats interval%s.", was_relay ? " in 6 "
                 "hours from now" : "");
      } else {
        geoip_bridge_stats_term();
        log_info(LD_GENERAL, "We are no longer acting as a bridge.  "
                 "Forgetting GeoIP stats.");
      }
    }
  }

  return 0;
}

/** Fetch the active option list, and take relay statistics actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Sets <b>*print_notice_out</b> if we enabled stats, and need to print
 * a stats log using options_act_relay_stats_msg().
 *
 * If loading the GeoIP file failed, sets DirReqStatistics and
 * EntryStatistics to 0. This breaks the normalization/act ordering
 * introduced in 29211.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_stats(const or_options_t *old_options,
                        bool *print_notice_out)
{
  if (BUG(!print_notice_out))
    return -1;

  or_options_t *options = get_options_mutable();

  if (options->CellStatistics || options->DirReqStatistics ||
      options->EntryStatistics || options->ExitPortStatistics ||
      options->ConnDirectionStatistics ||
      options->HiddenServiceStatistics) {
    time_t now = time(NULL);
    int print_notice = 0;

    if ((!old_options || !old_options->CellStatistics) &&
        options->CellStatistics) {
      rep_hist_buffer_stats_init(now);
      print_notice = 1;
    }
    if ((!old_options || !old_options->DirReqStatistics) &&
        options->DirReqStatistics) {
      if (geoip_is_loaded(AF_INET)) {
        geoip_dirreq_stats_init(now);
        print_notice = 1;
      } else {
        /* disable statistics collection since we have no geoip file */
        /* 29211: refactor to avoid the normalisation/act inversion */
        options->DirReqStatistics = 0;
        if (options->ORPort_set)
          log_notice(LD_CONFIG, "Configured to measure directory request "
                                "statistics, but no GeoIP database found. "
                                "Please specify a GeoIP database using the "
                                "GeoIPFile option.");
      }
    }
    if ((!old_options || !old_options->EntryStatistics) &&
        options->EntryStatistics && !should_record_bridge_info(options)) {
      /* If we get here, we've started recording bridge info when we didn't
       * do so before.  Note that "should_record_bridge_info()" will
       * always be false at this point, because of the earlier block
       * that cleared EntryStatistics when public_server_mode() was false.
       * We're leaving it in as defensive programming. */
      if (geoip_is_loaded(AF_INET) || geoip_is_loaded(AF_INET6)) {
        geoip_entry_stats_init(now);
        print_notice = 1;
      } else {
        options->EntryStatistics = 0;
        log_notice(LD_CONFIG, "Configured to measure entry node "
                              "statistics, but no GeoIP database found. "
                              "Please specify a GeoIP database using the "
                              "GeoIPFile option.");
      }
    }
    if ((!old_options || !old_options->ExitPortStatistics) &&
        options->ExitPortStatistics) {
      rep_hist_exit_stats_init(now);
      print_notice = 1;
    }
    if ((!old_options || !old_options->ConnDirectionStatistics) &&
        options->ConnDirectionStatistics) {
    }
    if ((!old_options || !old_options->HiddenServiceStatistics) &&
        options->HiddenServiceStatistics) {
      log_info(LD_CONFIG, "Configured to measure hidden service statistics.");
      rep_hist_hs_stats_init(now);
    }
    if (print_notice)
      *print_notice_out = 1;
  }

  /* If we used to have statistics enabled but we just disabled them,
     stop gathering them.  */
  if (old_options && old_options->CellStatistics &&
      !options->CellStatistics)
    rep_hist_buffer_stats_term();
  if (old_options && old_options->DirReqStatistics &&
      !options->DirReqStatistics)
    geoip_dirreq_stats_term();
  if (old_options && old_options->EntryStatistics &&
      !options->EntryStatistics)
    geoip_entry_stats_term();
  if (old_options && old_options->HiddenServiceStatistics &&
      !options->HiddenServiceStatistics)
    rep_hist_hs_stats_term();
  if (old_options && old_options->ExitPortStatistics &&
      !options->ExitPortStatistics)
    rep_hist_exit_stats_term();
  if (old_options && old_options->ConnDirectionStatistics &&
      !options->ConnDirectionStatistics)

  return 0;
}

/** Print a notice about relay/dirauth stats being enabled. */
void
options_act_relay_stats_msg(void)
{
  log_notice(LD_CONFIG, "Configured to measure statistics. Look for "
             "the *-stats files that will first be written to the "
             "data directory in 24 hours from now.");
}

/** Fetch the active option list, and take relay descriptor actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_desc(const or_options_t *old_options)
{
  const or_options_t *options = get_options();

  /* Since our options changed, we might need to regenerate and upload our
   * server descriptor.
   */
  if (!old_options ||
      options_transition_affects_descriptor(old_options, options))
    mark_my_descriptor_dirty("config change");

  return 0;
}

/** Fetch the active option list, and take relay DoS actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_dos(const or_options_t *old_options)
{
  const or_options_t *options = get_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();
  }

  return 0;
}

/** Fetch the active option list, and take dirport actions based on
 * it. All of the things we do should survive being done repeatedly. If
 * present, <b>old_options</b> contains the previous value of the options.
 *
 * Return 0 if all goes well, return -1 if it's time to die.
 *
 * Note: We haven't moved all the "act on new configuration" logic
 * into the options_act* functions yet.  Some is still in do_hup() and other
 * places.
 */
int
options_act_relay_dir(const or_options_t *old_options)
{
  (void)old_options;

  const or_options_t *options = get_options();

  if (!public_server_mode(options))
    return 0;

  /* Load the webpage we're going to serve every time someone asks for '/' on
     our DirPort. */
  tor_free(global_dirfrontpagecontents);
  if (options->DirPortFrontPage) {
    global_dirfrontpagecontents =
      read_file_to_str(options->DirPortFrontPage, 0, NULL);
    if (!global_dirfrontpagecontents) {
      log_warn(LD_CONFIG,
               "DirPortFrontPage file '%s' not found. Continuing anyway.",
               options->DirPortFrontPage);
    }
  }

  return 0;
}