diff options
author | Nick Mathewson <nickm@torproject.org> | 2017-07-13 17:23:37 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2017-07-13 17:23:37 -0400 |
commit | ef4ea864ea66059f7fd71ac68cfe6067f32149f1 (patch) | |
tree | ef4e15abb8f5b846269479ba7cdc8320602f903d /src/test | |
parent | 62d241ad22b173ee908ad9cc2c55f5b10d22c2d9 (diff) | |
parent | 965e3a6628f26d5fb1422fb04aa12e807537a32a (diff) | |
download | tor-ef4ea864ea66059f7fd71ac68cfe6067f32149f1.tar.gz tor-ef4ea864ea66059f7fd71ac68cfe6067f32149f1.zip |
Merge remote-tracking branch 'dgoulet/ticket21979_032_04'
Diffstat (limited to 'src/test')
-rw-r--r-- | src/test/hs_build_address.py | 37 | ||||
-rw-r--r-- | src/test/include.am | 1 | ||||
-rw-r--r-- | src/test/test.c | 1 | ||||
-rw-r--r-- | src/test/test.h | 1 | ||||
-rw-r--r-- | src/test/test_helpers.c | 42 | ||||
-rw-r--r-- | src/test/test_helpers.h | 1 | ||||
-rw-r--r-- | src/test/test_hs.c | 22 | ||||
-rw-r--r-- | src/test/test_hs_config.c | 477 | ||||
-rw-r--r-- | src/test/test_hs_service.c | 249 |
9 files changed, 816 insertions, 15 deletions
diff --git a/src/test/hs_build_address.py b/src/test/hs_build_address.py new file mode 100644 index 0000000000..7be9c8b85a --- /dev/null +++ b/src/test/hs_build_address.py @@ -0,0 +1,37 @@ +import sys +import hashlib +import struct +import base64 + +# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires +# the pysha3 package (pip install pysha3). +if sys.version_info < (3, 6): + import sha3 + +# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0 +# used the old Keccak implementation. During the finalization of SHA3, NIST +# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function +# stayed the same. pysha3 1.0 provides the previous Keccak hash, too. +TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51" +if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest(): + print("pysha3 version is < 1.0. Please install from:") + print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3") + sys.exit(1) + +# Checksum is built like so: +# CHECKSUM = SHA3(".onion checksum" || PUBKEY || VERSION) +PREFIX = ".onion checksum".encode() +# 32 bytes ed25519 pubkey. +PUBKEY = ("\x42" * 32).encode() +# Version 3 is proposal224 +VERSION = 3 + +data = struct.pack('15s32sb', PREFIX, PUBKEY, VERSION) +checksum = hashlib.sha3_256(data).digest() + +# Onion address is built like so: +# onion_address = base32(PUBKEY || CHECKSUM || VERSION) + ".onion" +address = struct.pack('!32s2sb', PUBKEY, checksum, VERSION) +onion_addr = base64.b32encode(address).decode().lower() + +print("%s" % (onion_addr)) diff --git a/src/test/include.am b/src/test/include.am index e7a2e0278b..2e448c8b39 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -114,6 +114,7 @@ src_test_test_SOURCES = \ src/test/test_guardfraction.c \ src/test/test_extorport.c \ src/test/test_hs.c \ + src/test/test_hs_config.c \ src/test/test_hs_service.c \ src/test/test_hs_client.c \ src/test/test_hs_intropoint.c \ diff --git a/src/test/test.c b/src/test/test.c index 31b3db3a4f..b6b11ce94a 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1213,6 +1213,7 @@ struct testgroup_t testgroups[] = { { "extorport/", extorport_tests }, { "legacy_hs/", hs_tests }, { "hs_cache/", hs_cache }, + { "hs_config/", hs_config_tests }, { "hs_descriptor/", hs_descriptor }, { "hs_service/", hs_service_tests }, { "hs_client/", hs_client_tests }, diff --git a/src/test/test.h b/src/test/test.h index 4de0da99fb..9b2a0b842f 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -207,6 +207,7 @@ extern struct testcase_t guardfraction_tests[]; extern struct testcase_t extorport_tests[]; extern struct testcase_t hs_tests[]; extern struct testcase_t hs_cache[]; +extern struct testcase_t hs_config_tests[]; extern struct testcase_t hs_descriptor[]; extern struct testcase_t hs_service_tests[]; extern struct testcase_t hs_client_tests[]; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index 22d9de3f5b..e885d27815 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -7,18 +7,21 @@ */ #define ROUTERLIST_PRIVATE +#define CONFIG_PRIVATE #define CONNECTION_PRIVATE #define MAIN_PRIVATE #include "orconfig.h" #include "or.h" +#include "buffers.h" +#include "config.h" +#include "confparse.h" #include "connection.h" #include "main.h" +#include "nodelist.h" #include "relay.h" #include "routerlist.h" -#include "nodelist.h" -#include "buffers.h" #include "test.h" #include "test_helpers.h" @@ -239,3 +242,38 @@ test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose) return NULL; } +/* Helper function to parse a set of torrc options in a text format and return + * a newly allocated or_options_t object containing the configuration. On + * error, NULL is returned indicating that the conf couldn't be parsed + * properly. */ +or_options_t * +helper_parse_options(const char *conf) +{ + int ret = 0; + char *msg = NULL; + or_options_t *opt = NULL; + config_line_t *line = NULL; + + /* Kind of pointless to call this with a NULL value. */ + tt_assert(conf); + + opt = options_new(); + tt_assert(opt); + ret = config_get_lines(conf, &line, 1); + if (ret != 0) { + goto done; + } + ret = config_assign(&options_format, opt, line, 0, &msg); + if (ret != 0) { + goto done; + } + + done: + config_free_lines(line); + if (ret != 0) { + or_options_free(opt); + opt = NULL; + } + return opt; +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 96a4b594eb..847104a40a 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -24,6 +24,7 @@ int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, connection_t *test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose); +or_options_t *helper_parse_options(const char *conf); extern const char TEST_DESCRIPTORS[]; diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 178f94d273..093c7ec35e 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -10,6 +10,7 @@ #define CIRCUITBUILD_PRIVATE #define RENDCOMMON_PRIVATE #define RENDSERVICE_PRIVATE +#define HS_SERVICE_PRIVATE #include "or.h" #include "test.h" @@ -661,17 +662,8 @@ test_single_onion_poisoning(void *arg) smartlist_t *services = smartlist_new(); char *poison_path = NULL; - /* No services, no service to verify, no problem! */ - mock_options->HiddenServiceSingleHopMode = 0; - mock_options->HiddenServiceNonAnonymousMode = 0; - ret = rend_config_services(mock_options, 1); - tt_assert(ret == 0); - - /* Either way, no problem. */ mock_options->HiddenServiceSingleHopMode = 1; mock_options->HiddenServiceNonAnonymousMode = 1; - ret = rend_config_services(mock_options, 1); - tt_assert(ret == 0); /* Create the data directory, and, if the correct bit in arg is set, * create a directory for that service. @@ -726,8 +718,10 @@ test_single_onion_poisoning(void *arg) tt_assert(ret == 0); /* Add the first service */ - ret = rend_service_check_dir_and_add(services, mock_options, service_1, 0); - tt_assert(ret == 0); + 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! */ @@ -805,8 +799,10 @@ test_single_onion_poisoning(void *arg) tt_assert(ret == 0); /* Now add the second service: it has no key and no poison file */ - ret = rend_service_check_dir_and_add(services, mock_options, service_2, 0); - tt_assert(ret == 0); + 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; diff --git a/src/test/test_hs_config.c b/src/test/test_hs_config.c new file mode 100644 index 0000000000..343ce9f2f8 --- /dev/null +++ b/src/test/test_hs_config.c @@ -0,0 +1,477 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_config.c + * \brief Test hidden service configuration functionality. + */ + +#define CONFIG_PRIVATE +#define HS_SERVICE_PRIVATE + +#include "test.h" +#include "test_helpers.h" +#include "log_test_helpers.h" + +#include "config.h" +#include "hs_common.h" +#include "hs_config.h" +#include "hs_service.h" +#include "rendservice.h" + +static int +helper_config_service(const char *conf, int validate_only) +{ + int ret = 0; + or_options_t *options = NULL; + tt_assert(conf); + options = helper_parse_options(conf); + tt_assert(options); + ret = hs_config_service_all(options, validate_only); + done: + or_options_free(options); + return ret; +} + +static void +test_invalid_service(void *arg) +{ + int ret; + + (void) arg; + + /* Try with a missing port configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 1\n"; /* Wrong not supported version. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceVersion must be between 2 and 3"); + teardown_capture_of_logs(); + } + + /* Bad value of HiddenServiceAllowUnknownPorts. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServiceAllowUnknownPorts 2\n"; /* Should be 0 or 1. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceAllowUnknownPorts must be " + "between 0 and 1, not 2"); + teardown_capture_of_logs(); + } + + /* Bad value of HiddenServiceDirGroupReadable */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServiceDirGroupReadable 2\n"; /* Should be 0 or 1. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceDirGroupReadable must be " + "between 0 and 1, not 2"); + teardown_capture_of_logs(); + } + + /* Bad value of HiddenServiceMaxStreamsCloseCircuit */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServiceMaxStreamsCloseCircuit 2\n"; /* Should be 0 or 1. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceMaxStreamsCloseCircuit must " + "be between 0 and 1, not 2"); + teardown_capture_of_logs(); + } + + /* Too much max streams. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceMaxStreams 65536\n"; /* One too many. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceMaxStreams must be between " + "0 and 65535, not 65536"); + teardown_capture_of_logs(); + } + + /* Duplicate directory directive. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 81\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("Another hidden service is already " + "configured for directory"); + teardown_capture_of_logs(); + } + + /* Bad port. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 65536\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("Missing or invalid port"); + teardown_capture_of_logs(); + } + + /* Out of order directives. */ + { + const char *conf = + "HiddenServiceVersion 2\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServicePort 80\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceVersion with no preceding " + "HiddenServiceDir directive"); + teardown_capture_of_logs(); + } + + done: + ; +} + +static void +test_valid_service(void *arg) +{ + int ret; + + (void) arg; + + /* Mix of v2 and v3. Still valid. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 81\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 82\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_invalid_service_v2(void *arg) +{ + int validate_only = 1, ret; + + (void) arg; + + /* Try with a missing port configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("with no ports configured."); + teardown_capture_of_logs(); + } + + /* Too many introduction points. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceNumIntroductionPoints 11\n"; /* One too many. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceNumIntroductionPoints should " + "be between 0 and 10, not 11"); + teardown_capture_of_logs(); + } + + /* Too little introduction points. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceNumIntroductionPoints -1\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceNumIntroductionPoints should " + "be between 0 and 10, not -1"); + teardown_capture_of_logs(); + } + + /* Bad authorized client type. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceAuthorizeClient blah alice,bob\n"; /* blah is no good. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceAuthorizeClient contains " + "unrecognized auth-type"); + teardown_capture_of_logs(); + } + + done: + ; +} + +static void +test_valid_service_v2(void *arg) +{ + int ret; + + (void) arg; + + /* Valid complex configuration. Basic client authorization. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServicePort 22 localhost:22\n" + "HiddenServicePort 42 unix:/path/to/socket\n" + "HiddenServiceAuthorizeClient basic alice,bob,eve\n" + "HiddenServiceAllowUnknownPorts 1\n" + "HiddenServiceMaxStreams 42\n" + "HiddenServiceMaxStreamsCloseCircuit 0\n" + "HiddenServiceDirGroupReadable 1\n" + "HiddenServiceNumIntroductionPoints 7\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + /* Valid complex configuration. Stealth client authorization. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 65535\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServicePort 9000 unix:/path/to/socket\n" + "HiddenServiceAuthorizeClient stealth charlie,romeo\n" + "HiddenServiceAllowUnknownPorts 0\n" + "HiddenServiceMaxStreams 42\n" + "HiddenServiceMaxStreamsCloseCircuit 0\n" + "HiddenServiceDirGroupReadable 1\n" + "HiddenServiceNumIntroductionPoints 8\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_invalid_service_v3(void *arg) +{ + int validate_only = 1, ret; + + (void) arg; + + /* Try with a missing port configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 3\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("with no ports configured."); + teardown_capture_of_logs(); + } + + /* Too many introduction points. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 80\n" + "HiddenServiceNumIntroductionPoints 21\n"; /* One too many. */ + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceNumIntroductionPoints must " + "be between 3 and 20, not 21."); + teardown_capture_of_logs(); + } + + /* Too little introduction points. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 80\n" + "HiddenServiceNumIntroductionPoints 1\n"; + setup_full_capture_of_logs(LOG_WARN); + ret = helper_config_service(conf, validate_only); + tt_int_op(ret, OP_EQ, -1); + expect_log_msg_containing("HiddenServiceNumIntroductionPoints must " + "be between 3 and 20, not 1."); + teardown_capture_of_logs(); + } + + done: + ; +} + +static void +test_valid_service_v3(void *arg) +{ + int ret; + + (void) arg; + + /* Valid complex configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 80\n" + "HiddenServicePort 22 localhost:22\n" + "HiddenServicePort 42 unix:/path/to/socket\n" + "HiddenServiceAllowUnknownPorts 1\n" + "HiddenServiceMaxStreams 42\n" + "HiddenServiceMaxStreamsCloseCircuit 0\n" + "HiddenServiceDirGroupReadable 1\n" + "HiddenServiceNumIntroductionPoints 7\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + /* Valid complex configuration. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 65535\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServicePort 9000 unix:/path/to/socket\n" + "HiddenServiceAllowUnknownPorts 0\n" + "HiddenServiceMaxStreams 42\n" + "HiddenServiceMaxStreamsCloseCircuit 0\n" + "HiddenServiceDirGroupReadable 1\n" + "HiddenServiceNumIntroductionPoints 20\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + /* Mix of v2 and v3. Still valid. */ + { + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 80\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 81\n" + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n" + "HiddenServiceVersion 2\n" + "HiddenServicePort 82\n"; + ret = helper_config_service(conf, 1); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_staging_service_v3(void *arg) +{ + int ret; + + (void) arg; + + /* We don't validate a service object, this is the service test that are in + * charge of doing so. We just check for the stable state after + * registration. */ + + hs_init(); + + /* Time for a valid v3 service that should get staged. */ + const char *conf = + "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n" + "HiddenServiceVersion 3\n" + "HiddenServicePort 65535\n" + "HiddenServicePort 22 1.1.1.1:22\n" + "HiddenServicePort 9000 unix:/path/to/socket\n" + "HiddenServiceAllowUnknownPorts 0\n" + "HiddenServiceMaxStreams 42\n" + "HiddenServiceMaxStreamsCloseCircuit 0\n" + "HiddenServiceDirGroupReadable 1\n" + "HiddenServiceNumIntroductionPoints 20\n"; + ret = helper_config_service(conf, 0); + tt_int_op(ret, OP_EQ, 0); + /* Ok, we have a service in our map! Registration went well. */ + tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1); + /* Make sure we don't have a magic v2 service out of this. */ + tt_int_op(num_rend_services(), OP_EQ, 0); + + done: + hs_free_all(); +} + +struct testcase_t hs_config_tests[] = { + /* Invalid service not specific to any version. */ + { "invalid_service", test_invalid_service, TT_FORK, + NULL, NULL }, + { "valid_service", test_valid_service, TT_FORK, + NULL, NULL }, + + /* Test case only for version 2. */ + { "invalid_service_v2", test_invalid_service_v2, TT_FORK, + NULL, NULL }, + { "valid_service_v2", test_valid_service_v2, TT_FORK, + NULL, NULL }, + + /* Test case only for version 3. */ + { "invalid_service_v3", test_invalid_service_v3, TT_FORK, + NULL, NULL }, + { "valid_service_v3", test_valid_service_v3, TT_FORK, + NULL, NULL }, + + /* Test service staging. */ + { "staging_service_v3", test_staging_service_v3, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 17772f1df0..24fca08a14 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -8,14 +8,17 @@ #define CIRCUITBUILD_PRIVATE #define CIRCUITLIST_PRIVATE +#define CONFIG_PRIVATE #define CONNECTION_PRIVATE #define CRYPTO_PRIVATE #define HS_COMMON_PRIVATE +#define HS_SERVICE_PRIVATE #define HS_INTROPOINT_PRIVATE #define MAIN_PRIVATE #define TOR_CHANNEL_INTERNAL_ #include "test.h" +#include "test_helpers.h" #include "log_test_helpers.h" #include "rend_test_helpers.h" @@ -26,8 +29,10 @@ #include "circuituse.h" #include "config.h" #include "connection.h" +#include "crypto.h" #include "hs_circuit.h" #include "hs_common.h" +#include "hs_config.h" #include "hs_ident.h" #include "hs_intropoint.h" #include "hs_ntor.h" @@ -35,6 +40,25 @@ #include "main.h" #include "rendservice.h" +/* Trunnel */ +#include "hs/cell_establish_intro.h" + +/* Helper: from a set of options in conf, configure a service which will add + * it to the staging list of the HS subsytem. */ +static int +helper_config_service(const char *conf) +{ + int ret = 0; + or_options_t *options = NULL; + tt_assert(conf); + options = helper_parse_options(conf); + tt_assert(options); + ret = hs_config_service_all(options, 0); + done: + or_options_free(options); + return ret; +} + /** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we * parse it from the receiver side. */ static void @@ -207,6 +231,85 @@ test_hs_ntor(void *arg) tt_mem_op(client_hs_ntor_rend_cell_keys.ntor_key_seed, OP_EQ, service_hs_ntor_rend_cell_keys.ntor_key_seed, DIGEST256_LEN); + done: + ; +} + +static void +test_validate_address(void *arg) +{ + int ret; + + (void) arg; + + /* Address too short and too long. */ + setup_full_capture_of_logs(LOG_WARN); + ret = hs_address_is_valid("blah"); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("has an invalid length"); + teardown_capture_of_logs(); + + setup_full_capture_of_logs(LOG_WARN); + ret = hs_address_is_valid( + "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnadb"); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("has an invalid length"); + teardown_capture_of_logs(); + + /* Invalid checksum (taken from prop224) */ + setup_full_capture_of_logs(LOG_WARN); + ret = hs_address_is_valid( + "l5satjgud6gucryazcyvyvhuxhr74u6ygigiuyixe3a6ysis67ororad"); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("invalid checksum"); + teardown_capture_of_logs(); + + setup_full_capture_of_logs(LOG_WARN); + ret = hs_address_is_valid( + "btojiu7nu5y5iwut64eufevogqdw4wmqzugnoluw232r4t3ecsfv37ad"); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("invalid checksum"); + teardown_capture_of_logs(); + + /* Non base32 decodable string. */ + setup_full_capture_of_logs(LOG_WARN); + ret = hs_address_is_valid( + "????????????????????????????????????????????????????????"); + tt_int_op(ret, OP_EQ, 0); + expect_log_msg_containing("can't be decoded"); + teardown_capture_of_logs(); + + /* Valid address. */ + ret = hs_address_is_valid( + "p3xnclpu4mu22dwaurjtsybyqk4xfjmcfz6z62yl24uwmhjatiwnlnad"); + tt_int_op(ret, OP_EQ, 1); + + done: + ; +} + +static void +test_build_address(void *arg) +{ + int ret; + char onion_addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + ed25519_public_key_t pubkey; + + (void) arg; + + /* The following has been created with hs_build_address.py script that + * follows proposal 224 specification to build an onion address. */ + static const char *test_addr = + "ijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbeeqscijbezhid"; + + /* Let's try to build the same onion address that the script can do. Key is + * a long set of very random \x42 :). */ + memset(&pubkey, '\x42', sizeof(pubkey)); + hs_build_address(&pubkey, HS_VERSION_THREE, onion_addr); + tt_str_op(test_addr, OP_EQ, onion_addr); + /* Validate that address. */ + ret = hs_address_is_valid(onion_addr); + tt_int_op(ret, OP_EQ, 1); done: ; @@ -315,6 +418,144 @@ test_e2e_rend_circuit_setup(void *arg) circuit_free(TO_CIRCUIT(or_circ)); } +static void +test_load_keys(void *arg) +{ + int ret; + char *conf = NULL; + char *hsdir_v2 = tor_strdup(get_fname("hs2")); + char *hsdir_v3 = tor_strdup(get_fname("hs3")); + char addr[HS_SERVICE_ADDR_LEN_BASE32 + 1]; + + (void) arg; + + /* We'll register two services, a v2 and a v3, then we'll load keys and + * validate that both are in a correct state. */ + + hs_init(); + +#define conf_fmt \ + "HiddenServiceDir %s\n" \ + "HiddenServiceVersion %d\n" \ + "HiddenServicePort 65535\n" + + /* v2 service. */ + tor_asprintf(&conf, conf_fmt, hsdir_v2, HS_VERSION_TWO); + ret = helper_config_service(conf); + tor_free(conf); + tt_int_op(ret, OP_EQ, 0); + /* This one should now be registered into the v2 list. */ + tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 0); + tt_int_op(num_rend_services(), OP_EQ, 1); + + /* v3 service. */ + tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE); + ret = helper_config_service(conf); + tor_free(conf); + tt_int_op(ret, OP_EQ, 0); + /* It's in staging? */ + tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1); + + /* Load the keys for these. After that, the v3 service should be registered + * in the global map. */ + hs_service_load_all_keys(); + tt_int_op(get_hs_service_map_size(), OP_EQ, 1); + hs_service_t *s = get_first_service(); + tt_assert(s); + + /* Ok we have the service object. Validate few things. */ + tt_assert(!tor_mem_is_zero(s->onion_address, sizeof(s->onion_address))); + tt_int_op(hs_address_is_valid(s->onion_address), OP_EQ, 1); + tt_assert(!tor_mem_is_zero((char *) s->keys.identity_sk.seckey, + ED25519_SECKEY_LEN)); + tt_assert(!tor_mem_is_zero((char *) s->keys.identity_pk.pubkey, + ED25519_PUBKEY_LEN)); + /* Check onion address from identity key. */ + hs_build_address(&s->keys.identity_pk, s->config.version, addr); + tt_int_op(hs_address_is_valid(addr), OP_EQ, 1); + tt_str_op(addr, OP_EQ, s->onion_address); + + done: + tor_free(hsdir_v2); + tor_free(hsdir_v3); + hs_free_all(); +} + +static void +test_access_service(void *arg) +{ + int ret; + char *conf = NULL; + char *hsdir_v3 = tor_strdup(get_fname("hs3")); + hs_service_ht *global_map; + + (void) arg; + + /* We'll register two services, a v2 and a v3, then we'll load keys and + * validate that both are in a correct state. */ + + hs_init(); + +#define conf_fmt \ + "HiddenServiceDir %s\n" \ + "HiddenServiceVersion %d\n" \ + "HiddenServicePort 65535\n" + + /* v3 service. */ + tor_asprintf(&conf, conf_fmt, hsdir_v3, HS_VERSION_THREE); + ret = helper_config_service(conf); + tor_free(conf); + tt_int_op(ret, OP_EQ, 0); + /* It's in staging? */ + tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1); + + /* Load the keys for these. After that, the v3 service should be registered + * in the global map. */ + hs_service_load_all_keys(); + tt_int_op(get_hs_service_map_size(), OP_EQ, 1); + hs_service_t *s = get_first_service(); + tt_assert(s); + global_map = get_hs_service_map(); + tt_assert(global_map); + + /* From here, we'll try the service accessors. */ + hs_service_t *query = find_service(global_map, &s->keys.identity_pk); + tt_assert(query); + tt_mem_op(query, OP_EQ, s, sizeof(hs_service_t)); + /* Remove service, check if it actually works and then put it back. */ + remove_service(global_map, s); + tt_int_op(get_hs_service_map_size(), OP_EQ, 0); + query = find_service(global_map, &s->keys.identity_pk); + tt_assert(!query); + + /* Register back the service in the map. */ + ret = register_service(global_map, s); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(get_hs_service_map_size(), OP_EQ, 1); + /* Twice should fail. */ + ret = register_service(global_map, s); + tt_int_op(ret, OP_EQ, -1); + /* Modify key of service and we should be able to put it back in. */ + s->keys.identity_pk.pubkey[1] = '\x42'; + ret = register_service(global_map, s); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(get_hs_service_map_size(), OP_EQ, 2); + /* Remove service from map so we don't double free on cleanup. */ + remove_service(global_map, s); + tt_int_op(get_hs_service_map_size(), OP_EQ, 1); + query = find_service(global_map, &s->keys.identity_pk); + tt_assert(!query); + /* Let's try to remove twice for fun. */ + setup_full_capture_of_logs(LOG_WARN); + remove_service(global_map, s); + expect_log_msg_containing("Could not find service in the global map"); + teardown_capture_of_logs(); + + done: + tor_free(hsdir_v3); + hs_free_all(); +} + struct testcase_t hs_service_tests[] = { { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, NULL, NULL }, @@ -326,6 +567,14 @@ struct testcase_t hs_service_tests[] = { NULL, NULL }, { "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK, NULL, NULL }, + { "build_address", test_build_address, TT_FORK, + NULL, NULL }, + { "validate_address", test_validate_address, TT_FORK, + NULL, NULL }, + { "load_keys", test_load_keys, TT_FORK, + NULL, NULL }, + { "access_service", test_access_service, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; |