From 09b12c40947ea496c0bfaeeafba7540925c17a32 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 2 Feb 2017 15:26:04 -0500 Subject: [PATCH] test: Add v3 service load keys and accessors Signed-off-by: David Goulet --- src/or/hs_service.c | 27 ++++-- src/or/hs_service.h | 14 ++++ src/test/test_hs_service.c | 166 +++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+), 8 deletions(-) diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 854ce9e541..bfce7804ab 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -57,11 +57,6 @@ hs_service_ht_hash(const hs_service_t *service) sizeof(service->keys.identity_pk.pubkey)); } -/* For the service global hash map, we define a specific type for it which - * will make it safe to use and specific to some controlled parameters such as - * the hashing function and how to compare services. */ -typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht; - /* This is _the_ global hash map of hidden services which indexed the service * contained in it by master public identity key which is roughly the onion * address of the service. */ @@ -82,7 +77,7 @@ HT_GENERATE2(hs_service_ht, hs_service_t, hs_service_node, * if found else NULL. It is also possible to set a directory path in the * search query. If pk is NULL, then it will be set to zero indicating the * hash table to compare the directory path instead. */ -static hs_service_t * +STATIC hs_service_t * find_service(hs_service_ht *map, const ed25519_public_key_t *pk) { hs_service_t dummy_service = {0}; @@ -95,7 +90,7 @@ find_service(hs_service_ht *map, const ed25519_public_key_t *pk) /* Register the given service in the given map. If the service already exists * in the map, -1 is returned. On success, 0 is returned and the service * ownership has been transfered to the global map. */ -static int +STATIC int register_service(hs_service_ht *map, hs_service_t *service) { tor_assert(map); @@ -113,7 +108,7 @@ register_service(hs_service_ht *map, hs_service_t *service) /* Remove a given service from the given map. If service is NULL or the * service key is unset, return gracefully. */ -static void +STATIC void remove_service(hs_service_ht *map, hs_service_t *service) { hs_service_t *elm; @@ -804,5 +799,21 @@ get_hs_service_staging_list_size(void) return smartlist_len(hs_service_staging_list); } +STATIC hs_service_ht * +get_hs_service_map(void) +{ + return hs_service_map; +} + +STATIC hs_service_t * +get_first_service(void) +{ + hs_service_t **obj = HT_START(hs_service_ht, hs_service_map); + if (obj == NULL) { + return NULL; + } + return *obj; +} + #endif /* TOR_UNIT_TESTS */ diff --git a/src/or/hs_service.h b/src/or/hs_service.h index cd154d3fe9..a98884f6bc 100644 --- a/src/or/hs_service.h +++ b/src/or/hs_service.h @@ -200,6 +200,11 @@ typedef struct hs_service_t { } hs_service_t; +/* For the service global hash map, we define a specific type for it which + * will make it safe to use and specific to some controlled parameters such as + * the hashing function and how to compare services. */ +typedef HT_HEAD(hs_service_ht, hs_service_t) hs_service_ht; + /* API */ /* Global initializer and cleanup function. */ @@ -228,8 +233,17 @@ get_establish_intro_payload(uint8_t *buf, size_t buf_len, #ifdef TOR_UNIT_TESTS +/* Useful getters for unit tests. */ STATIC unsigned int get_hs_service_map_size(void); STATIC int get_hs_service_staging_list_size(void); +STATIC hs_service_ht *get_hs_service_map(void); +STATIC hs_service_t *get_first_service(void); + +/* Service accessors. */ +STATIC hs_service_t *find_service(hs_service_ht *map, + const ed25519_public_key_t *pk); +STATIC void remove_service(hs_service_ht *map, hs_service_t *service); +STATIC int register_service(hs_service_ht *map, hs_service_t *service); #endif /* TOR_UNIT_TESTS */ diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index e081b7f2f3..c695b90bc9 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 @@ -394,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->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 }, @@ -409,6 +571,10 @@ struct testcase_t hs_service_tests[] = { 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 };