Esempio n. 1
0
/** Create or open a new storage directory at <b>dirname</b>, with
 * capacity for up to <b>max_files</b> files.
 */
storage_dir_t *
storage_dir_new(const char *dirname, int max_files)
{
  if (check_private_dir(dirname, CPD_CREATE, NULL) < 0)
    return NULL;

  storage_dir_t *d = tor_malloc_zero(sizeof(storage_dir_t));
  d->directory = tor_strdup(dirname);
  d->max_files = max_files;
  return d;
}
Esempio n. 2
0
static void
test_routerkeys_write_fingerprint(void *arg)
{
  crypto_pk_t *key = pk_generate(2);
  or_options_t *options = get_options_mutable();
  const char *ddir = get_fname("write_fingerprint");
  char *cp = NULL, *cp2 = NULL;
  char fp[FINGERPRINT_LEN+1];

  (void)arg;

  tt_assert(key);

  options->ORPort_set = 1; /* So that we can get the server ID key */
  tor_free(options->DataDirectory);
  options->DataDirectory = tor_strdup(ddir);
  options->Nickname = tor_strdup("haflinger");
  set_server_identity_key(key);
  set_client_identity_key(crypto_pk_dup_key(key));

  tt_int_op(0, OP_EQ, check_private_dir(ddir, CPD_CREATE, NULL));
  tt_int_op(crypto_pk_cmp_keys(get_server_identity_key(),key),OP_EQ,0);

  /* Write fingerprint file */
  tt_int_op(0, OP_EQ, router_write_fingerprint(0));
  cp = read_file_to_str(get_fname("write_fingerprint/fingerprint"),
                        0, NULL);
  crypto_pk_get_fingerprint(key, fp, 0);
  tor_asprintf(&cp2, "haflinger %s\n", fp);
  tt_str_op(cp, OP_EQ, cp2);
  tor_free(cp);
  tor_free(cp2);

  /* Write hashed-fingerprint file */
  tt_int_op(0, OP_EQ, router_write_fingerprint(1));
  cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"),
                        0, NULL);
  crypto_pk_get_hashed_fingerprint(key, fp);
  tor_asprintf(&cp2, "haflinger %s\n", fp);
  tt_str_op(cp, OP_EQ, cp2);
  tor_free(cp);
  tor_free(cp2);

  /* Replace outdated file */
  write_str_to_file(get_fname("write_fingerprint/hashed-fingerprint"),
                    "junk goes here", 0);
  tt_int_op(0, OP_EQ, router_write_fingerprint(1));
  cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"),
                        0, NULL);
  crypto_pk_get_hashed_fingerprint(key, fp);
  tor_asprintf(&cp2, "haflinger %s\n", fp);
  tt_str_op(cp, OP_EQ, cp2);
  tor_free(cp);
  tor_free(cp2);

 done:
  crypto_pk_free(key);
  set_client_identity_key(NULL);
  tor_free(cp);
  tor_free(cp2);
}
Esempio n. 3
0
/**
 * Running as a server: load, reload, or refresh our ed25519 keys and
 * certificates, creating and saving new ones as needed.
 */
int
load_ed_keys(const or_options_t *options, time_t now)
{
  ed25519_keypair_t *id = NULL;
  ed25519_keypair_t *sign = NULL;
  ed25519_keypair_t *auth = NULL;
  const ed25519_keypair_t *sign_signing_key_with_id = NULL;
  const ed25519_keypair_t *use_signing = NULL;
  const tor_cert_t *check_signing_cert = NULL;
  tor_cert_t *sign_cert = NULL;
  tor_cert_t *auth_cert = NULL;

#define FAIL(msg) do {                          \
    log_warn(LD_OR, (msg));                     \
    goto err;                                   \
  } while (0)
#define SET_KEY(key, newval) do {               \
    if ((key) != (newval))                      \
      ed25519_keypair_free(key);                \
    key = (newval);                             \
  } while (0)
#define SET_CERT(cert, newval) do {             \
    if ((cert) != (newval))                     \
      tor_cert_free(cert);                      \
    cert = (newval);                            \
  } while (0)
#define EXPIRES_SOON(cert, interval)            \
  (!(cert) || (cert)->valid_until < now + (interval))

  /* XXXX support encrypted identity keys fully */

  /* First try to get the signing key to see how it is. */
  {
    char *fname =
      options_get_datadir_fname2(options, "keys", "ed25519_signing");
    sign = ed_key_init_from_file(
               fname,
               INIT_ED_KEY_NEEDCERT|
               INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT,
               LOG_INFO,
               NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert);
    tor_free(fname);
    check_signing_cert = sign_cert;
    use_signing = sign;
  }

  if (!use_signing && master_signing_key) {
    check_signing_cert = signing_key_cert;
    use_signing = master_signing_key;
  }

  const int offline_master =
    options->OfflineMasterKey && options->command != CMD_KEYGEN;
  const int need_new_signing_key =
    NULL == use_signing ||
    EXPIRES_SOON(check_signing_cert, 0) ||
    (options->command == CMD_KEYGEN && ! options->change_key_passphrase);
  const int want_new_signing_key =
    need_new_signing_key ||
    EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop);

  /* We can only create a master key if we haven't been told that the
   * master key will always be offline.  Also, if we have a signing key,
   * then we shouldn't make a new master ID key. */
  const int can_make_master_id_key = !offline_master &&
    NULL == use_signing;

  if (need_new_signing_key) {
    log_notice(LD_OR, "It looks like I need to generate and sign a new "
               "medium-term signing key, because %s. To do that, I need to "
               "load%s the permanent master identity key.",
            (NULL == use_signing) ? "I don't have one" :
            EXPIRES_SOON(check_signing_cert, 0) ? "the one I have is expired" :
               "you asked me to make one with --keygen",
            can_make_master_id_key ? " (or create)" : "");
  } else if (want_new_signing_key && !offline_master) {
    log_notice(LD_OR, "It looks like I should try to generate and sign a "
               "new medium-term signing key, because the one I have is "
               "going to expire soon. To do that, I'm going to have to try to "
               "load the permanent master identity key.");
  } else if (want_new_signing_key) {
    log_notice(LD_OR, "It looks like I should try to generate and sign a "
               "new medium-term signing key, because the one I have is "
               "going to expire soon. But OfflineMasterKey is set, so I "
               "won't try to load a permanent master identity key is set. "
               "You will need to use 'tor --keygen' make a new signing key "
               "and certificate.");
  }

  {
    uint32_t flags =
      (INIT_ED_KEY_SPLIT|
       INIT_ED_KEY_EXTRA_STRONG|INIT_ED_KEY_NO_REPAIR);
    if (can_make_master_id_key)
      flags |= INIT_ED_KEY_CREATE;
    if (! need_new_signing_key)
      flags |= INIT_ED_KEY_MISSING_SECRET_OK;
    if (! want_new_signing_key || offline_master)
      flags |= INIT_ED_KEY_OMIT_SECRET;
    if (offline_master)
      flags |= INIT_ED_KEY_OFFLINE_SECRET;
    if (options->command == CMD_KEYGEN)
      flags |= INIT_ED_KEY_TRY_ENCRYPTED;

    /* Check the key directory */
    if (check_private_dir(options->DataDirectory, CPD_CREATE, options->User)) {
      log_err(LD_OR, "Can't create/check datadirectory %s",
              options->DataDirectory);
      goto err;
    }
    char *fname = get_datadir_fname("keys");
    if (check_private_dir(fname, CPD_CREATE, options->User) < 0) {
      log_err(LD_OR, "Problem creating/checking key directory %s", fname);
      tor_free(fname);
      goto err;
    }
    tor_free(fname);
    if (options->master_key_fname) {
      fname = tor_strdup(options->master_key_fname);
      flags |= INIT_ED_KEY_EXPLICIT_FNAME;
    } else {
      fname = options_get_datadir_fname2(options, "keys", "ed25519_master_id");
    }
    id = ed_key_init_from_file(
             fname,
             flags,
             LOG_WARN, NULL, 0, 0, 0, NULL);
    tor_free(fname);
    if (!id) {
      if (need_new_signing_key) {
        if (offline_master)
          FAIL("Can't load master identity key; OfflineMasterKey is set.");
        else
          FAIL("Missing identity key");
      } else {
        log_warn(LD_OR, "Master public key was absent; inferring from "
                 "public key in signing certificate and saving to disk.");
        tor_assert(check_signing_cert);
        id = tor_malloc_zero(sizeof(*id));
        memcpy(&id->pubkey, &check_signing_cert->signing_key,
               sizeof(ed25519_public_key_t));
        fname = options_get_datadir_fname2(options, "keys",
                                           "ed25519_master_id_public_key");
        if (ed25519_pubkey_write_to_file(&id->pubkey, fname, "type0") < 0) {
          log_warn(LD_OR, "Error while attempting to write master public key "
                   "to disk");
          tor_free(fname);
          goto err;
        }
        tor_free(fname);
      }
    }
    if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey)))
      sign_signing_key_with_id = NULL;
    else
      sign_signing_key_with_id = id;
  }

  if (master_identity_key &&
      !ed25519_pubkey_eq(&id->pubkey, &master_identity_key->pubkey)) {
    FAIL("Identity key on disk does not match key we loaded earlier!");
  }

  if (need_new_signing_key && NULL == sign_signing_key_with_id)
    FAIL("Can't load master key make a new signing key.");

  if (sign_cert) {
    if (! sign_cert->signing_key_included)
      FAIL("Loaded a signing cert with no key included!");
    if (! ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey))
      FAIL("The signing cert we have was not signed with the master key "
           "we loaded!");
    if (tor_cert_checksig(sign_cert, &id->pubkey, 0) < 0)
      FAIL("The signing cert we loaded was not signed correctly!");
  }

  if (want_new_signing_key && sign_signing_key_with_id) {
    uint32_t flags = (INIT_ED_KEY_CREATE|
                      INIT_ED_KEY_REPLACE|
                      INIT_ED_KEY_EXTRA_STRONG|
                      INIT_ED_KEY_NEEDCERT|
                      INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
    char *fname =
      options_get_datadir_fname2(options, "keys", "ed25519_signing");
    ed25519_keypair_free(sign);
    tor_cert_free(sign_cert);
    sign = ed_key_init_from_file(fname,
                                 flags, LOG_WARN,
                                 sign_signing_key_with_id, now,
                                 options->SigningKeyLifetime,
                                 CERT_TYPE_ID_SIGNING, &sign_cert);
    tor_free(fname);
    if (!sign)
      FAIL("Missing signing key");
    use_signing = sign;

    tor_assert(sign_cert->signing_key_included);
    tor_assert(ed25519_pubkey_eq(&sign_cert->signing_key, &id->pubkey));
    tor_assert(ed25519_pubkey_eq(&sign_cert->signed_key, &sign->pubkey));
  } else if (want_new_signing_key) {
    static ratelim_t missing_master = RATELIM_INIT(3600);
    log_fn_ratelim(&missing_master, LOG_WARN, LD_OR,
                   "Signing key will expire soon, but I can't load the "
                   "master key to sign a new one!");
  }

  tor_assert(use_signing);

  /* At this point we no longer need our secret identity key.  So wipe
   * it, if we loaded it in the first place. */
  memwipe(id->seckey.seckey, 0, sizeof(id->seckey));

  if (options->command == CMD_KEYGEN)
    goto end;

  if (!rsa_ed_crosscert && server_mode(options)) {
    uint8_t *crosscert;
    ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey,
                                                   get_server_identity_key(),
                                                   now+10*365*86400,/*XXXX*/
                                                   &crosscert);
    rsa_ed_crosscert_len = crosscert_len;
    rsa_ed_crosscert = crosscert;
  }

  if (!current_auth_key ||
      EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) {
    auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
                      now,
                      options->TestingAuthKeyLifetime,
                      CERT_TYPE_SIGNING_AUTH, &auth_cert);

    if (!auth)
      FAIL("Can't create auth key");
  }

  /* We've generated or loaded everything.  Put them in memory. */

 end:
  if (! master_identity_key) {
    SET_KEY(master_identity_key, id);
  } else {
    tor_free(id);
  }
  if (sign) {
    SET_KEY(master_signing_key, sign);
    SET_CERT(signing_key_cert, sign_cert);
  }
  if (auth) {
    SET_KEY(current_auth_key, auth);
    SET_CERT(auth_key_cert, auth_cert);
  }

  return 0;
 err:
  ed25519_keypair_free(id);
  ed25519_keypair_free(sign);
  ed25519_keypair_free(auth);
  tor_cert_free(sign_cert);
  tor_cert_free(auth_cert);
  return -1;
}
Esempio n. 4
0
/** Run unit tests for private dir permission enforcement logic. */
static void
test_checkdir_perms(void *testdata)
{
  (void)testdata;
  or_options_t *options = get_options_mutable();
  const char *subdir = "test_checkdir";
  char *testdir = NULL;
  cpd_check_t  cpd_chkopts;
  cpd_check_t  unix_create_opts;
  cpd_check_t  unix_verify_optsmask;
  struct stat st;

  umask(022);

  /* setup data directory before tests. */
  tor_free(options->DataDirectory);
  options->DataDirectory = tor_strdup(get_fname(subdir));
  tt_int_op(mkdir(options->DataDirectory, 0750), OP_EQ, 0);

  /* test: create new dir, no flags. */
  testdir = get_datadir_fname("checkdir_new_none");
  cpd_chkopts = CPD_CREATE;
  unix_verify_optsmask = 0077;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: create new dir, CPD_GROUP_OK option set. */
  testdir = get_datadir_fname("checkdir_new_groupok");
  cpd_chkopts = CPD_CREATE|CPD_GROUP_OK;
  unix_verify_optsmask = 0077;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: should get an error on existing dir with
           wrong perms */
  testdir = get_datadir_fname("checkdir_new_groupok_err");
  tt_int_op(0, OP_EQ, mkdir(testdir, 027));
  cpd_chkopts = CPD_CHECK_MODE_ONLY|CPD_CREATE|CPD_GROUP_OK;
  tt_int_op_nowin(-1, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tor_free(testdir);

  /* test: create new dir, CPD_GROUP_READ option set. */
  testdir = get_datadir_fname("checkdir_new_groupread");
  cpd_chkopts = CPD_CREATE|CPD_GROUP_READ;
  unix_verify_optsmask = 0027;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: check existing dir created with defaults,
            and verify with CPD_CREATE only. */
  testdir = get_datadir_fname("checkdir_exists_none");
  cpd_chkopts = CPD_CREATE;
  unix_create_opts = 0700;
  (void)unix_create_opts;
  unix_verify_optsmask = 0077;
  tt_int_op(0, OP_EQ, mkdir(testdir, unix_create_opts));
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: check existing dir created with defaults,
            and verify with CPD_GROUP_OK option set. */
  testdir = get_datadir_fname("checkdir_exists_groupok");
  cpd_chkopts = CPD_CREATE;
  unix_verify_optsmask = 0077;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  cpd_chkopts = CPD_GROUP_OK;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: check existing dir created with defaults,
            and verify with CPD_GROUP_READ option set. */
  testdir = get_datadir_fname("checkdir_exists_groupread");
  cpd_chkopts = CPD_CREATE;
  unix_verify_optsmask = 0027;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  cpd_chkopts = CPD_GROUP_READ;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: check existing dir created with CPD_GROUP_READ,
            and verify with CPD_GROUP_OK option set. */
  testdir = get_datadir_fname("checkdir_existsread_groupok");
  cpd_chkopts = CPD_CREATE|CPD_GROUP_READ;
  unix_verify_optsmask = 0027;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  cpd_chkopts = CPD_GROUP_OK;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));
  tor_free(testdir);

  /* test: check existing dir created with CPD_GROUP_READ,
            and verify with CPD_GROUP_READ option set. */
  testdir = get_datadir_fname("checkdir_existsread_groupread");
  cpd_chkopts = CPD_CREATE|CPD_GROUP_READ;
  unix_verify_optsmask = 0027;
  tt_int_op(0, OP_EQ, check_private_dir(testdir, cpd_chkopts, NULL));
  tt_int_op(0, OP_EQ, stat(testdir, &st));
  tt_int_op_nowin(0, OP_EQ, (st.st_mode & unix_verify_optsmask));

 done:
  tor_free(testdir);
}
Esempio n. 5
0
File: test_hs.c Progetto: 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);
}
Esempio n. 6
0
// check profile line; if line == 0, this was generated from a command line option
// return 1 if the command is to be added to the linked list of profile commands
// return 0 if the command was already executed inside the function
int profile_check_line(char *ptr, int lineno) {
	// seccomp, caps, private
	if (strcmp(ptr, "seccomp") == 0) {
		arg_seccomp = 1;
		return 0;
	}
	else if (strcmp(ptr, "caps") == 0) {
		arg_caps = 1;
		return 0;
	}
	else if (strcmp(ptr, "private") == 0) {
		arg_private = 1;
		return 0;
	}
	
	// seccomp list
	if (strncmp(ptr, "seccomp ", 8) == 0) {
		arg_seccomp = 1;
#ifdef HAVE_SECCOMP
		arg_seccomp_list = strdup(ptr + 8);
		if (!arg_seccomp_list)
			errExit("strdup");
		// verify seccomp list and exit if problems
		if (syscall_check_list(arg_seccomp_list, NULL))
			exit(1);
#endif
		return 0;
	}
	
	// private directory
	if (strncmp(ptr, "private ", 8) == 0) {
		cfg.home_private = ptr + 8;
		check_private_dir();
		arg_private = 1;
		return 0;
	}

	// filesystem bind
	if (strncmp(ptr, "bind ", 5) == 0) {
		if (getuid() != 0) {
			fprintf(stderr, "Error: --bind option is available only if running as root\n");
			exit(1);
		}

		// extract two directories
		char *dname1 = ptr + 5;
		char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories
		if (dname2 == NULL) {
			fprintf(stderr, "Error: mising second directory for bind\n");
			exit(1);
		}
		
		// check directories
		check_file_name(dname1, lineno);
		check_file_name(dname2, lineno);
		
		// insert comma back
		*(dname2 - 1) = ',';
		return 1;
	}

	// rlimit
	if (strncmp(ptr, "rlimit", 6) == 0) {
		if (strncmp(ptr, "rlimit-nofile ", 14) == 0) {
			ptr += 14;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_nofile);
			arg_rlimit_nofile = 1;
		}
		else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) {
			ptr += 13;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_nproc);
			arg_rlimit_nproc = 1;
		}
		else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
			ptr += 13;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_fsize);
			arg_rlimit_fsize = 1;
		}
		else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
			ptr += 18;
			if (not_unsigned(ptr)) {
				fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
				exit(1);
			}
			sscanf(ptr, "%u", &cfg.rlimit_sigpending);
			arg_rlimit_sigpending = 1;
		}
		else {
			fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
			exit(1);
		}
		
		return 0;		
	}

	// rest of filesystem
	if (strncmp(ptr, "blacklist ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "read-only ", 10) == 0)
		ptr += 10;
	else if (strncmp(ptr, "tmpfs ", 6) == 0)
		ptr += 6;
	else {
		if (lineno == 0)
			fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr);
		else
			fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
		exit(1);
	}

	// some characters just don't belong in filenames
	check_file_name(ptr, lineno);
	return 1;
}