diff options
author | Nick Mathewson <nickm@torproject.org> | 2016-09-13 10:20:08 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2016-09-13 10:20:08 -0400 |
commit | 9f0cb5af1559d4dab0a49017bdd5f37b0af588f8 (patch) | |
tree | 9e968c8978b2bee6b11726f9b9a84d97dc3508e0 /src/test | |
parent | 4b182dfc237ba4457b654a0dbc124f721024dab2 (diff) | |
parent | f311c9ffa2d9cdb64b92e4ceab5b3c582b976228 (diff) | |
download | tor-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.c | 207 | ||||
-rw-r--r-- | src/test/test_options.c | 149 |
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), |