示例#1
0
static void
test_rend_service_parse_port_config(void *arg)
{
  const char *sep = ",";
  rend_service_port_config_t *cfg = NULL;
  char *err_msg = NULL;

  (void)arg;

  /* Test "VIRTPORT" only. */
  cfg = rend_service_parse_port_config("80", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is IPv4:port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,192.0.2.1:8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is IPv6:port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* XXX: Someone should add tests for AF_UNIX targets if supported. */

  /* Test empty config. */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("", sep, &err_msg);
  tt_assert(!cfg);
  tt_assert(err_msg);

  /* Test invalid port. */
  tor_free(err_msg);
  cfg = rend_service_parse_port_config("90001", sep, &err_msg);
  tt_assert(!cfg);
  tt_assert(err_msg);

 done:
  rend_service_port_config_free(cfg);
  tor_free(err_msg);
}
示例#2
0
文件: test_hs.c 项目: ageis/tor
/* Test that single onion poisoning works. */
static void
test_single_onion_poisoning(void *arg)
{
  or_options_t opt;
  mock_options = &opt;
  reset_options(mock_options, &mock_get_options_calls);
  MOCK(get_options, mock_get_options);

  int ret = -1;
  intptr_t create_dir_mask = (intptr_t)arg;
  /* Get directories with a random suffix so we can repeat the tests */
  mock_options->DataDirectory = tor_strdup(get_fname_rnd("test_data_dir"));
  rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t));
  char *dir1 = tor_strdup(get_fname_rnd("test_hs_dir1"));
  rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t));
  char *dir2 = tor_strdup(get_fname_rnd("test_hs_dir2"));
  smartlist_t *services = smartlist_new();
  char *poison_path = NULL;
  char *err_msg = NULL;

  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;

  /* Create the data directory, and, if the correct bit in arg is set,
   * create a directory for that service.
   * The data directory is required for the lockfile, which is used when
   * loading keys. */
  ret = check_private_dir(mock_options->DataDirectory, CPD_CREATE, NULL);
  tt_int_op(ret, OP_EQ, 0);
  if (create_dir_mask & CREATE_HS_DIR1) {
    ret = check_private_dir(dir1, CPD_CREATE, NULL);
    tt_int_op(ret, OP_EQ, 0);
  }
  if (create_dir_mask & CREATE_HS_DIR2) {
    ret = check_private_dir(dir2, CPD_CREATE, NULL);
    tt_int_op(ret, OP_EQ, 0);
  }

  service_1->directory = dir1;
  service_2->directory = dir2;
  /* The services own the directory pointers now */
  dir1 = dir2 = NULL;
  /* Add port to service 1 */
  service_1->ports = smartlist_new();
  service_2->ports = smartlist_new();
  rend_service_port_config_t *port1 = rend_service_parse_port_config("80", " ",
                                                                     &err_msg);
  tt_assert(port1);
  tt_ptr_op(err_msg, OP_EQ, NULL);
  smartlist_add(service_1->ports, port1);

  rend_service_port_config_t *port2 = rend_service_parse_port_config("90", " ",
                                                                     &err_msg);
  /* Add port to service 2 */
  tt_assert(port2);
  tt_ptr_op(err_msg, OP_EQ, NULL);
  smartlist_add(service_2->ports, port2);

  /* No services, a service to verify, no problem! */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Either way, no problem. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Add the first service */
  ret = hs_check_service_private_dir(mock_options->User, service_1->directory,
                                     service_1->dir_group_readable, 1);
  tt_int_op(ret, OP_EQ, 0);
  smartlist_add(services, service_1);
  /* But don't add the second service yet. */

  /* Service directories, but no previous keys, no problem! */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Either way, no problem. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Poison! Poison! Poison!
   * This can only be done in HiddenServiceSingleHopMode. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  /* Poisoning twice is a no-op. */
  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Poisoned service directories, but no previous keys, no problem! */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Either way, no problem. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Now add some keys, and we'll have a problem. */
  ret = rend_service_load_all_keys(services);
  tt_int_op(ret, OP_EQ, 0);

  /* Poisoned service directories with previous keys are not allowed. */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_LT, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* But they are allowed if we're in non-anonymous mode. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Re-poisoning directories with existing keys is a no-op, because
   * directories with existing keys are ignored. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  /* And it keeps the poison. */
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Now add the second service: it has no key and no poison file */
  ret = hs_check_service_private_dir(mock_options->User, service_2->directory,
                                     service_2->dir_group_readable, 1);
  tt_int_op(ret, OP_EQ, 0);
  smartlist_add(services, service_2);

  /* A new service, and an existing poisoned service. Not ok. */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_LT, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* But ok to add in non-anonymous mode. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Now remove the poisoning from the first service, and we have the opposite
   * problem. */
  poison_path = rend_service_sos_poison_path(service_1);
  tt_assert(poison_path);
  ret = unlink(poison_path);
  tt_int_op(ret, OP_EQ, 0);

  /* Unpoisoned service directories with previous keys are ok, as are empty
   * directories. */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* But the existing unpoisoned key is not ok in non-anonymous mode, even if
   * there is an empty service. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_LT, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Poisoning directories with existing keys is a no-op, because directories
   * with existing keys are ignored. But the new directory should poison. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  /* And the old directory remains unpoisoned. */
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_LT, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* And the new directory should be ignored, because it has no key. */
  mock_options->HiddenServiceSingleHopMode = 0;
  mock_options->HiddenServiceNonAnonymousMode = 0;
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

  /* Re-poisoning directories without existing keys is a no-op. */
  mock_options->HiddenServiceSingleHopMode = 1;
  mock_options->HiddenServiceNonAnonymousMode = 1;
  ret = rend_service_poison_new_single_onion_dir(service_1, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  ret = rend_service_poison_new_single_onion_dir(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);
  /* And the old directory remains unpoisoned. */
  ret = rend_service_verify_single_onion_poison(service_1, mock_options);
  tt_int_op(ret, OP_LT, 0);
  ret = rend_service_verify_single_onion_poison(service_2, mock_options);
  tt_int_op(ret, OP_EQ, 0);

 done:
  /* The test harness deletes the directories at exit */
  tor_free(poison_path);
  tor_free(dir1);
  tor_free(dir2);
  smartlist_free(services);
  rend_service_free(service_1);
  rend_service_free(service_2);
  UNMOCK(get_options);
  tor_free(mock_options->DataDirectory);
  tor_free(err_msg);
}
static void
test_rend_service_parse_port_config(void *arg)
{
  const char *sep = ",";
  rend_service_port_config_t *cfg = NULL;
  char *err_msg = NULL;

  (void)arg;

  /* Test "VIRTPORT" only. */
  cfg = rend_service_parse_port_config("80", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is IPv4:port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,192.0.2.1:8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);

  /* Test "VIRTPORT,TARGET" (Target is IPv6:port). */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);
  rend_service_port_config_free(cfg);
  cfg = NULL;

  /* XXX: Someone should add tests for AF_UNIX targets if supported. */

  /* Test empty config. */
  rend_service_port_config_free(cfg);
  cfg = rend_service_parse_port_config("", sep, &err_msg);
  tt_assert(!cfg);
  tt_assert(err_msg);

  /* Test invalid port. */
  tor_free(err_msg);
  cfg = rend_service_parse_port_config("90001", sep, &err_msg);
  tt_assert(!cfg);
  tt_assert(err_msg);
  tor_free(err_msg);

  /* unix port */
  cfg = NULL;

  /* quoted unix port */
  tor_free(err_msg);
  cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
                                       " ", &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);
  rend_service_port_config_free(cfg);
  cfg = NULL;

  /* quoted unix port */
  tor_free(err_msg);
  cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar\"",
                                       " ", &err_msg);
  tt_assert(cfg);
  tt_assert(!err_msg);
  rend_service_port_config_free(cfg);
  cfg = NULL;

  /* quoted unix port, missing end quote */
  cfg = rend_service_parse_port_config("100 unix:\"/tmp/foo bar",
                                       " ", &err_msg);
  tt_assert(!cfg);
  tt_str_op(err_msg, OP_EQ, "Couldn't process address <unix:\"/tmp/foo bar> "
            "from hidden service configuration");
  tor_free(err_msg);

  /* bogus IP address */
  cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000",
                                       " ", &err_msg);
  tt_assert(!cfg);
  tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port "
            "configuration.");
  tor_free(err_msg);

  /* bogus port port */
  cfg = rend_service_parse_port_config("100 99999",
                                       " ", &err_msg);
  tt_assert(!cfg);
  tt_str_op(err_msg, OP_EQ, "Unparseable or out-of-range port \"99999\" "
            "in hidden service port configuration.");
  tor_free(err_msg);

 done:
  rend_service_port_config_free(cfg);
  tor_free(err_msg);
}
示例#4
0
文件: hs_config.c 项目: jfrazelle/tor
/* Configure a service using the given options in line_ and options. This is
 * called for any service regardless of its version which means that all
 * directives in this function are generic to any service version. This
 * function will also check the validity of the service directory path.
 *
 * The line_ must be pointing to the directive directly after a
 * HiddenServiceDir. That way, when hitting the next HiddenServiceDir line or
 * reaching the end of the list of lines, we know that we have to stop looking
 * for more options.
 *
 * Return 0 on success else -1. */
static int
config_generic_service(const config_line_t *line_,
                       const or_options_t *options,
                       hs_service_t *service)
{
  int dir_seen = 0;
  const config_line_t *line;
  hs_service_config_t *config;
  /* If this is set, we've seen a duplicate of this option. Keep the string
   * so we can log the directive. */
  const char *dup_opt_seen = NULL;
  /* These variables will tell us if we ever have duplicate. */
  int have_version = 0, have_allow_unknown_ports = 0;
  int have_dir_group_read = 0, have_max_streams = 0;
  int have_max_streams_close = 0;

  tor_assert(line_);
  tor_assert(options);
  tor_assert(service);

  /* Makes thing easier. */
  config = &service->config;

  /* The first line starts with HiddenServiceDir so we consider what's next is
   * the configuration of the service. */
  for (line = line_; line ; line = line->next) {
    int ok = 0;

    /* This indicate that we have a new service to configure. */
    if (!strcasecmp(line->key, "HiddenServiceDir")) {
      /* This function only configures one service at a time so if we've
       * already seen one, stop right now. */
      if (dir_seen) {
        break;
      }
      /* Ok, we've seen one and we are about to configure it. */
      dir_seen = 1;
      config->directory_path = tor_strdup(line->value);
      log_info(LD_CONFIG, "HiddenServiceDir=%s. Configuring...",
               escaped(config->directory_path));
      continue;
    }
    if (BUG(!dir_seen)) {
      goto err;
    }
    /* Version of the service. */
    if (!strcasecmp(line->key, "HiddenServiceVersion")) {
      service->config.version =
        (uint32_t) helper_parse_uint64(line->key, line->value, HS_VERSION_MIN,
                                       HS_VERSION_MAX, &ok);
      if (!ok || have_version) {
        if (have_version)
          dup_opt_seen = line->key;
        goto err;
      }
      have_version = service->config.hs_version_explicitly_set = 1;
      continue;
    }
    /* Virtual port. */
    if (!strcasecmp(line->key, "HiddenServicePort")) {
      char *err_msg = NULL;
      /* XXX: Can we rename this? */
      rend_service_port_config_t *portcfg =
        rend_service_parse_port_config(line->value, " ", &err_msg);
      if (!portcfg) {
        if (err_msg) {
          log_warn(LD_CONFIG, "%s", err_msg);
        }
        tor_free(err_msg);
        goto err;
      }
      tor_assert(!err_msg);
      smartlist_add(config->ports, portcfg);
      log_info(LD_CONFIG, "HiddenServicePort=%s for %s",
               line->value, escaped(config->directory_path));
      continue;
    }
    /* Do we allow unknown ports. */
    if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
      config->allow_unknown_ports =
        (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
      if (!ok || have_allow_unknown_ports) {
        if (have_allow_unknown_ports)
          dup_opt_seen = line->key;
        goto err;
      }
      have_allow_unknown_ports = 1;
      continue;
    }
    /* Directory group readable. */
    if (!strcasecmp(line->key, "HiddenServiceDirGroupReadable")) {
      config->dir_group_readable =
        (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
      if (!ok || have_dir_group_read) {
        if (have_dir_group_read)
          dup_opt_seen = line->key;
        goto err;
      }
      have_dir_group_read = 1;
      continue;
    }
    /* Maximum streams per circuit. */
    if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
      config->max_streams_per_rdv_circuit =
        helper_parse_uint64(line->key, line->value, 0,
                            HS_CONFIG_MAX_STREAMS_PER_RDV_CIRCUIT, &ok);
      if (!ok || have_max_streams) {
        if (have_max_streams)
          dup_opt_seen = line->key;
        goto err;
      }
      have_max_streams = 1;
      continue;
    }
    /* Maximum amount of streams before we close the circuit. */
    if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
      config->max_streams_close_circuit =
        (unsigned int) helper_parse_uint64(line->key, line->value, 0, 1, &ok);
      if (!ok || have_max_streams_close) {
        if (have_max_streams_close)
          dup_opt_seen = line->key;
        goto err;
      }
      have_max_streams_close = 1;
      continue;
    }
  }

  /* Check if we are configured in non anonymous mode meaning every service
   * becomes a single onion service. */
  if (rend_service_non_anonymous_mode_enabled(options)) {
    config->is_single_onion = 1;
    /* We will add support for IPv6-only v3 single onion services in a future
     * Tor version. This won't catch "ReachableAddresses reject *4", but that
     * option doesn't work anyway. */
    if (options->ClientUseIPv4 == 0 && config->version == HS_VERSION_THREE) {
      log_warn(LD_CONFIG, "IPv6-only v3 single onion services are not "
               "supported. Set HiddenServiceSingleHopMode 0 and "
               "HiddenServiceNonAnonymousMode 0, or set ClientUseIPv4 1.");
      goto err;
    }
  }

  /* Success */
  return 0;
 err:
  if (dup_opt_seen) {
    log_warn(LD_CONFIG, "Duplicate directive %s.", dup_opt_seen);
  }
  return -1;
}