summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2016-09-13 10:20:08 -0400
committerNick Mathewson <nickm@torproject.org>2016-09-13 10:20:08 -0400
commit9f0cb5af1559d4dab0a49017bdd5f37b0af588f8 (patch)
tree9e968c8978b2bee6b11726f9b9a84d97dc3508e0 /src/test
parent4b182dfc237ba4457b654a0dbc124f721024dab2 (diff)
parentf311c9ffa2d9cdb64b92e4ceab5b3c582b976228 (diff)
downloadtor-9f0cb5af1559d4dab0a49017bdd5f37b0af588f8.tar.gz
tor-9f0cb5af1559d4dab0a49017bdd5f37b0af588f8.zip
Merge branch 'feature-17178-v7-squashed-v2'
Diffstat (limited to 'src/test')
-rw-r--r--src/test/test_hs.c207
-rw-r--r--src/test/test_options.c149
2 files changed, 356 insertions, 0 deletions
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 1daa1552e9..a9cd315a82 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -8,12 +8,14 @@
#define CONTROL_PRIVATE
#define CIRCUITBUILD_PRIVATE
+#define RENDSERVICE_PRIVATE
#include "or.h"
#include "test.h"
#include "control.h"
#include "config.h"
#include "rendcommon.h"
+#include "rendservice.h"
#include "routerset.h"
#include "circuitbuild.h"
#include "test_helpers.h"
@@ -496,6 +498,209 @@ test_hs_auth_cookies(void *arg)
return;
}
+static int mock_get_options_calls = 0;
+static or_options_t *mock_options = NULL;
+
+static void
+reset_options(or_options_t *options, int *get_options_calls)
+{
+ memset(options, 0, sizeof(or_options_t));
+ options->TestingTorNetwork = 1;
+
+ *get_options_calls = 0;
+}
+
+static const or_options_t *
+mock_get_options(void)
+{
+ ++mock_get_options_calls;
+ tor_assert(mock_options);
+ return mock_options;
+}
+
+/* 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;
+ mock_options->DataDirectory = tor_strdup(get_fname("test_data_dir"));
+ rend_service_t *service_1 = tor_malloc_zero(sizeof(rend_service_t));
+ char *dir1 = tor_strdup(get_fname("test_hs_dir1"));
+ rend_service_t *service_2 = tor_malloc_zero(sizeof(rend_service_t));
+ char *dir2 = tor_strdup(get_fname("test_hs_dir2"));
+ smartlist_t *services = smartlist_new();
+
+ (void) arg;
+
+ /* No services, no problem! */
+ mock_options->HiddenServiceSingleHopMode = 0;
+ mock_options->HiddenServiceNonAnonymousMode = 0;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Either way, no problem. */
+ mock_options->HiddenServiceSingleHopMode = 1;
+ mock_options->HiddenServiceNonAnonymousMode = 1;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Create directories for both services */
+
+#ifdef _WIN32
+ ret = mkdir(mock_options->DataDirectory);
+ tt_assert(ret == 0);
+ ret = mkdir(dir1);
+ tt_assert(ret == 0);
+ ret = mkdir(dir2);
+#else
+ ret = mkdir(mock_options->DataDirectory, 0700);
+ tt_assert(ret == 0);
+ ret = mkdir(dir1, 0700);
+ tt_assert(ret == 0);
+ ret = mkdir(dir2, 0700);
+#endif
+ tt_assert(ret == 0);
+
+ service_1->directory = dir1;
+ service_2->directory = dir2;
+ 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_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Either way, no problem. */
+ mock_options->HiddenServiceSingleHopMode = 1;
+ mock_options->HiddenServiceNonAnonymousMode = 1;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 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_dirs(services);
+ tt_assert(ret == 0);
+ /* Poisoning twice is a no-op. */
+ ret = rend_service_poison_new_single_onion_dirs(services);
+ tt_assert(ret == 0);
+
+ /* Poisoned service directories, but no previous keys, no problem! */
+ mock_options->HiddenServiceSingleHopMode = 0;
+ mock_options->HiddenServiceNonAnonymousMode = 0;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Either way, no problem. */
+ mock_options->HiddenServiceSingleHopMode = 1;
+ mock_options->HiddenServiceNonAnonymousMode = 1;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Now add some keys, and we'll have a problem. */
+ ret = rend_service_load_all_keys(services);
+ tt_assert(ret == 0);
+
+ /* Poisoned service directories with previous keys are not allowed. */
+ mock_options->HiddenServiceSingleHopMode = 0;
+ mock_options->HiddenServiceNonAnonymousMode = 0;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret < 0);
+
+ /* But they are allowed if we're in non-anonymous mode. */
+ mock_options->HiddenServiceSingleHopMode = 1;
+ mock_options->HiddenServiceNonAnonymousMode = 1;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 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_dirs(services);
+ tt_assert(ret == 0);
+ /* And it keeps the poison. */
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Now add the second service: it has no key and no poison file */
+ 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_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret < 0);
+
+ /* But ok to add in non-anonymous mode. */
+ mock_options->HiddenServiceSingleHopMode = 1;
+ mock_options->HiddenServiceNonAnonymousMode = 1;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 0);
+
+ /* Now remove the poisoning from the first service, and we have the opposite
+ * problem. */
+ char *poison_path = rend_service_sos_poison_path(service_1);
+ ret = unlink(poison_path);
+ tor_free(poison_path);
+ tt_assert(ret == 0);
+
+ /* Unpoisoned service directories with previous keys are ok, as are empty
+ * directories. */
+ mock_options->HiddenServiceSingleHopMode = 0;
+ mock_options->HiddenServiceNonAnonymousMode = 0;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 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_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret < 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_dirs(services);
+ tt_assert(ret == 0);
+ /* And the old directory remains unpoisoned. */
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret < 0);
+
+ /* And the new directory should be ignored, because it has no key. */
+ mock_options->HiddenServiceSingleHopMode = 0;
+ mock_options->HiddenServiceNonAnonymousMode = 0;
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret == 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_dirs(services);
+ tt_assert(ret == 0);
+ /* And the old directory remains unpoisoned. */
+ ret = rend_service_list_verify_single_onion_poison(services, mock_options);
+ tt_assert(ret < 0);
+
+ done:
+ /* TODO: should we delete the directories here? */
+ rend_service_free(service_1);
+ rend_service_free(service_2);
+ smartlist_free(services);
+ UNMOCK(get_options);
+ tor_free(mock_options->DataDirectory);
+}
+
struct testcase_t hs_tests[] = {
{ "hs_rend_data", test_hs_rend_data, TT_FORK,
NULL, NULL },
@@ -508,6 +713,8 @@ struct testcase_t hs_tests[] = {
NULL, NULL },
{ "hs_auth_cookies", test_hs_auth_cookies, TT_FORK,
NULL, NULL },
+ { "single_onion_poisoning", test_single_onion_poisoning, TT_FORK,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_options.c b/src/test/test_options.c
index 0451b93738..9ce7c420a0 100644
--- a/src/test/test_options.c
+++ b/src/test/test_options.c
@@ -2750,6 +2750,154 @@ test_options_validate__rend(void *ignored)
}
static void
+test_options_validate__single_onion(void *ignored)
+{
+ (void)ignored;
+ int ret;
+ char *msg;
+ options_test_data_t *tdata = NULL;
+ int previous_log = setup_capture_of_logs(LOG_WARN);
+
+ /* Test that HiddenServiceSingleHopMode must come with
+ * HiddenServiceNonAnonymousMode */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HiddenServiceSingleHopMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceNonAnonymousMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HiddenServiceSingleHopMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceNonAnonymousMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ /* Test that SOCKSPort must come with Tor2webMode if
+ * HiddenServiceSingleHopMode is 1 */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode is incompatible with "
+ "using Tor as an anonymous client. Please set "
+ "Socks/Trans/NATD/DNSPort to 0, or HiddenServiceNonAnonymousMode "
+ "to 0, or use the non-anonymous Tor2webMode.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 0\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 0\n"
+ "Tor2webMode 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "SOCKSPort 5000\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "HiddenServiceNonAnonymousMode 1\n"
+ "Tor2webMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ /* Test that a hidden service can't be run with Tor2web
+ * Use HiddenServiceNonAnonymousMode instead of Tor2webMode, because
+ * Tor2webMode requires a compilation #define */
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceSingleHopMode set to 1.");
+ tor_free(msg);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, -1);
+ tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode does not provide any "
+ "server anonymity. It must be used with "
+ "HiddenServiceSingleHopMode set to 1.");
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+ free_options_test_data(tdata);
+
+ tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES
+ "HiddenServiceNonAnonymousMode 1\n"
+ "HiddenServiceDir /Library/Tor/var/lib/tor/hidden_service/\n"
+ "HiddenServicePort 80 127.0.0.1:8080\n"
+ "HiddenServiceSingleHopMode 1\n"
+ "SOCKSPort 0\n"
+ );
+ ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg);
+ tt_int_op(ret, OP_EQ, 0);
+ tt_ptr_op(msg, OP_EQ, NULL);
+
+ done:
+ policies_free_all();
+ teardown_capture_of_logs(previous_log);
+ free_options_test_data(tdata);
+ tor_free(msg);
+}
+
+static void
test_options_validate__accounting(void *ignored)
{
(void)ignored;
@@ -4371,6 +4519,7 @@ struct testcase_t options_tests[] = {
LOCAL_VALIDATE_TEST(port_forwarding),
LOCAL_VALIDATE_TEST(tor2web),
LOCAL_VALIDATE_TEST(rend),
+ LOCAL_VALIDATE_TEST(single_onion),
LOCAL_VALIDATE_TEST(accounting),
LOCAL_VALIDATE_TEST(proxy),
LOCAL_VALIDATE_TEST(control),