summaryrefslogtreecommitdiff
path: root/src/test/test_hs_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/test_hs_service.c')
-rw-r--r--src/test/test_hs_service.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c
index f8a465629a..6a061eaea4 100644
--- a/src/test/test_hs_service.c
+++ b/src/test/test_hs_service.c
@@ -34,6 +34,7 @@
#include "core/or/circuitlist.h"
#include "core/or/circuituse.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "lib/fs/dir.h"
#include "feature/dirauth/dirvote.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/nodelist/nodelist.h"
@@ -65,6 +66,13 @@
/* Trunnel */
#include "trunnel/hs/cell_establish_intro.h"
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
static networkstatus_t mock_ns;
static networkstatus_t *
@@ -220,6 +228,40 @@ helper_create_origin_circuit(int purpose, int flags)
return circ;
}
+/* Helper: Return a newly allocated authorized client object with
+ * and a newly generated public key. */
+static hs_service_authorized_client_t *
+helper_create_authorized_client(void)
+{
+ int ret;
+ hs_service_authorized_client_t *client;
+ curve25519_secret_key_t seckey;
+ client = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
+
+ ret = curve25519_secret_key_generate(&seckey, 0);
+ tt_int_op(ret, OP_EQ, 0);
+ curve25519_public_key_generate(&client->client_pk, &seckey);
+
+ done:
+ return client;
+}
+
+/* Helper: Return a newly allocated authorized client object with the
+ * same client name and the same public key as the given client. */
+static hs_service_authorized_client_t *
+helper_clone_authorized_client(const hs_service_authorized_client_t *client)
+{
+ hs_service_authorized_client_t *client_out;
+
+ tor_assert(client);
+
+ client_out = tor_malloc_zero(sizeof(hs_service_authorized_client_t));
+ memcpy(client_out->client_pk.public_key,
+ client->client_pk.public_key, CURVE25519_PUBKEY_LEN);
+
+ return client_out;
+}
+
/* Helper: Return a newly allocated service object with the identity keypair
* sets and the current descriptor. Then register it to the global map.
* Caller should us hs_free_all() to free this service or remove it from the
@@ -244,6 +286,26 @@ helper_create_service(void)
return service;
}
+/* Helper: Return a newly allocated service object with clients. */
+static hs_service_t *
+helper_create_service_with_clients(int num_clients)
+{
+ int i;
+ hs_service_t *service = helper_create_service();
+ tt_assert(service);
+ service->config.is_client_auth_enabled = 1;
+ service->config.clients = smartlist_new();
+
+ for (i = 0; i < num_clients; i++) {
+ hs_service_authorized_client_t *client;
+ client = helper_create_authorized_client();
+ smartlist_add(service->config.clients, client);
+ }
+
+ done:
+ return service;
+}
+
/* Helper: Return a newly allocated service intro point with two link
* specifiers, one IPv4 and one legacy ID set to As. */
static hs_service_intro_point_t *
@@ -303,6 +365,8 @@ test_load_keys(void *arg)
/* It's in staging? */
tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
+#undef conf_fmt
+
/* Load the keys for these. After that, the v3 service should be registered
* in the global map. */
hs_service_load_all_keys();
@@ -322,6 +386,9 @@ test_load_keys(void *arg)
tt_int_op(hs_address_is_valid(addr), OP_EQ, 1);
tt_str_op(addr, OP_EQ, s->onion_address);
+ /* Check that the is_client_auth_enabled is not set. */
+ tt_assert(!s->config.is_client_auth_enabled);
+
done:
tor_free(hsdir_v2);
tor_free(hsdir_v3);
@@ -329,6 +396,184 @@ test_load_keys(void *arg)
}
static void
+test_client_filename_is_valid(void *arg)
+{
+ (void) arg;
+
+ /* Valid file name. */
+ tt_assert(client_filename_is_valid("a.auth"));
+ /* Valid file name with special character. */
+ tt_assert(client_filename_is_valid("a-.auth"));
+ /* Invalid extension. */
+ tt_assert(!client_filename_is_valid("a.ath"));
+ /* Nothing before the extension. */
+ tt_assert(!client_filename_is_valid(".auth"));
+
+ done:
+ ;
+}
+
+static void
+test_parse_authorized_client(void *arg)
+{
+ hs_service_authorized_client_t *client = NULL;
+
+ (void) arg;
+
+ /* Valid authorized client. */
+ client = parse_authorized_client(
+ "descriptor:x25519:dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja");
+ tt_assert(client);
+
+ /* Wrong number of fields. */
+ tt_assert(!parse_authorized_client("a:b:c:d:e"));
+ /* Wrong auth type. */
+ tt_assert(!parse_authorized_client(
+ "x:x25519:dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja"));
+ /* Wrong key type. */
+ tt_assert(!parse_authorized_client(
+ "descriptor:x:dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja"));
+ /* Some malformed string. */
+ tt_assert(!parse_authorized_client("descriptor:x25519:aa=="));
+ tt_assert(!parse_authorized_client("descriptor:"));
+ tt_assert(!parse_authorized_client("descriptor:x25519"));
+ tt_assert(!parse_authorized_client("descriptor:x25519:"));
+ tt_assert(!parse_authorized_client(""));
+
+ done:
+ service_authorized_client_free(client);
+}
+
+static char *
+mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
+{
+ char *ret = NULL;
+
+ (void) flags;
+ (void) stat_out;
+
+ if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR
+ "authorized_clients" PATH_SEPARATOR
+ "client1.auth"))) {
+ ret = tor_strdup("descriptor:x25519:"
+ "dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja");
+ goto done;
+ }
+
+ if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR
+ "authorized_clients" PATH_SEPARATOR
+ "dummy.xxx"))) {
+ ret = tor_strdup("descriptor:x25519:"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ goto done;
+ }
+
+ if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR
+ "authorized_clients" PATH_SEPARATOR
+ "client2.auth"))) {
+ ret = tor_strdup("descriptor:x25519:"
+ "okoi2gml3wd6x7jganlk5d66xxyjgg24sxw4y7javx4giqr66zta");
+ goto done;
+ }
+
+ done:
+ return ret;
+}
+
+static smartlist_t *
+mock_tor_listdir(const char *dirname)
+{
+ smartlist_t *file_list = smartlist_new();
+
+ (void) dirname;
+
+ smartlist_add(file_list, tor_strdup("client1.auth"));
+ smartlist_add(file_list, tor_strdup("dummy.xxx"));
+ smartlist_add(file_list, tor_strdup("client2.auth"));
+
+ return file_list;
+}
+
+static void
+test_load_keys_with_client_auth(void *arg)
+{
+ int ret;
+ char *conf = NULL;
+ smartlist_t *pubkey_b32_list = smartlist_new();
+ char *hsdir_v3 = tor_strdup(get_fname("hs3"));
+ hs_service_t *service;
+
+ (void) arg;
+
+ hs_init();
+ smartlist_add(pubkey_b32_list, tor_strdup(
+ "dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja"));
+ smartlist_add(pubkey_b32_list, tor_strdup(
+ "okoi2gml3wd6x7jganlk5d66xxyjgg24sxw4y7javx4giqr66zta"));
+
+#define conf_fmt \
+ "HiddenServiceDir %s\n" \
+ "HiddenServiceVersion %d\n" \
+ "HiddenServicePort 65534\n"
+
+ 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);
+
+#undef conf_fmt
+
+ MOCK(read_file_to_str, mock_read_file_to_str);
+ MOCK(tor_listdir, mock_tor_listdir);
+
+ /* 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);
+
+ service = get_first_service();
+ tt_assert(service->config.clients);
+ tt_int_op(smartlist_len(service->config.clients), OP_EQ,
+ smartlist_len(pubkey_b32_list));
+
+ /* Test that the is_client_auth_enabled flag is set. */
+ tt_assert(service->config.is_client_auth_enabled);
+
+ /* Test that the keys in clients are correct. */
+ SMARTLIST_FOREACH_BEGIN(pubkey_b32_list, char *, pubkey_b32) {
+
+ curve25519_public_key_t pubkey;
+ /* This flag will be set if the key is found in clients. */
+ int is_found = 0;
+ base32_decode((char *) pubkey.public_key, sizeof(pubkey.public_key),
+ pubkey_b32, strlen(pubkey_b32));
+
+ SMARTLIST_FOREACH_BEGIN(service->config.clients,
+ hs_service_authorized_client_t *, client) {
+ if (tor_memeq(&pubkey, &client->client_pk, sizeof(pubkey))) {
+ is_found = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(client);
+
+ tt_assert(is_found);
+
+ } SMARTLIST_FOREACH_END(pubkey_b32);
+
+ done:
+ if (pubkey_b32_list) {
+ SMARTLIST_FOREACH(pubkey_b32_list, char *, s, tor_free(s));
+ }
+ smartlist_free(pubkey_b32_list);
+ tor_free(hsdir_v3);
+ hs_free_all();
+ UNMOCK(read_file_to_str);
+ UNMOCK(tor_listdir);
+}
+
+static void
test_access_service(void *arg)
{
int ret;
@@ -1371,6 +1616,90 @@ test_build_update_descriptors(void *arg)
nodelist_free_all();
}
+/** Test building descriptors. We use this separate function instead of
+ * using test_build_update_descriptors because that function is too complex
+ * and also too interactive. */
+static void
+test_build_descriptors(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ hs_init();
+
+ MOCK(get_or_state,
+ get_or_state_replacement);
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+
+ dummy_state = tor_malloc_zero(sizeof(or_state_t));
+
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 03:00:00 UTC",
+ &mock_ns.valid_after);
+ tt_int_op(ret, OP_EQ, 0);
+ ret = parse_rfc1123_time("Sat, 26 Oct 1985 04:00:00 UTC",
+ &mock_ns.fresh_until);
+ tt_int_op(ret, OP_EQ, 0);
+ voting_schedule_recalculate_timing(get_options(), mock_ns.valid_after);
+
+ /* Generate a valid number of fake auth clients when a client authorization
+ * is disabled. */
+ {
+ hs_service_t *service = helper_create_service();
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+ }
+
+ /* Generate a valid number of fake auth clients when the number of
+ * clients is zero. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(0);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 16);
+ }
+
+ /* Generate a valid number of fake auth clients when the number of
+ * clients is not a multiple of 16. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(20);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+ }
+
+ /* Do not generate any fake desc client when the number of clients is
+ * a multiple of 16 but not zero. */
+ {
+ hs_service_t *service = helper_create_service_with_clients(32);
+ service_descriptor_free(service->desc_current);
+ service->desc_current = NULL;
+
+ build_all_descriptors(now);
+ hs_desc_superencrypted_data_t *superencrypted;
+ superencrypted = &service->desc_current->desc->superencrypted_data;
+ tt_int_op(smartlist_len(superencrypted->clients), OP_EQ, 32);
+ }
+
+ done:
+ hs_free_all();
+}
+
static void
test_upload_descriptors(void *arg)
{
@@ -1556,11 +1885,137 @@ test_rendezvous1_parsing(void *arg)
UNMOCK(relay_send_command_from_edge_);
}
+static void
+test_authorized_client_config_equal(void *arg)
+{
+ int ret;
+ hs_service_config_t *config1, *config2;
+
+ (void) arg;
+
+ config1 = tor_malloc_zero(sizeof(*config1));
+ config2 = tor_malloc_zero(sizeof(*config2));
+
+ /* Both configs are empty. */
+ {
+ config1->clients = smartlist_new();
+ config2->clients = smartlist_new();
+
+ ret = service_authorized_client_config_equal(config1, config2);
+ tt_int_op(ret, OP_EQ, 1);
+
+ service_clear_config(config1);
+ service_clear_config(config2);
+ }
+
+ /* Both configs have exactly the same client config. */
+ {
+ config1->clients = smartlist_new();
+ config2->clients = smartlist_new();
+
+ hs_service_authorized_client_t *client1, *client2;
+ client1 = helper_create_authorized_client();
+ client2 = helper_create_authorized_client();
+
+ smartlist_add(config1->clients, client1);
+ smartlist_add(config1->clients, client2);
+
+ /* We should swap the order of clients here to test that the order
+ * does not matter. */
+ smartlist_add(config2->clients, helper_clone_authorized_client(client2));
+ smartlist_add(config2->clients, helper_clone_authorized_client(client1));
+
+ ret = service_authorized_client_config_equal(config1, config2);
+ tt_int_op(ret, OP_EQ, 1);
+
+ service_clear_config(config1);
+ service_clear_config(config2);
+ }
+
+ /* The numbers of clients in both configs are not equal. */
+ {
+ config1->clients = smartlist_new();
+ config2->clients = smartlist_new();
+
+ hs_service_authorized_client_t *client1, *client2;
+ client1 = helper_create_authorized_client();
+ client2 = helper_create_authorized_client();
+
+ smartlist_add(config1->clients, client1);
+ smartlist_add(config1->clients, client2);
+
+ smartlist_add(config2->clients, helper_clone_authorized_client(client1));
+
+ ret = service_authorized_client_config_equal(config1, config2);
+ tt_int_op(ret, OP_EQ, 0);
+
+ service_clear_config(config1);
+ service_clear_config(config2);
+ }
+
+ /* The first config has two distinct clients while the second config
+ * has two clients but they are duplicate. */
+ {
+ config1->clients = smartlist_new();
+ config2->clients = smartlist_new();
+
+ hs_service_authorized_client_t *client1, *client2;
+ client1 = helper_create_authorized_client();
+ client2 = helper_create_authorized_client();
+
+ smartlist_add(config1->clients, client1);
+ smartlist_add(config1->clients, client2);
+
+ smartlist_add(config2->clients, helper_clone_authorized_client(client1));
+ smartlist_add(config2->clients, helper_clone_authorized_client(client1));
+
+ ret = service_authorized_client_config_equal(config1, config2);
+ tt_int_op(ret, OP_EQ, 0);
+
+ service_clear_config(config1);
+ service_clear_config(config2);
+ }
+
+ /* Both configs have totally distinct clients. */
+ {
+ config1->clients = smartlist_new();
+ config2->clients = smartlist_new();
+
+ hs_service_authorized_client_t *client1, *client2, *client3, *client4;
+ client1 = helper_create_authorized_client();
+ client2 = helper_create_authorized_client();
+ client3 = helper_create_authorized_client();
+ client4 = helper_create_authorized_client();
+
+ smartlist_add(config1->clients, client1);
+ smartlist_add(config1->clients, client2);
+
+ smartlist_add(config2->clients, client3);
+ smartlist_add(config2->clients, client4);
+
+ ret = service_authorized_client_config_equal(config1, config2);
+ tt_int_op(ret, OP_EQ, 0);
+
+ service_clear_config(config1);
+ service_clear_config(config2);
+ }
+
+ done:
+ tor_free(config1);
+ tor_free(config2);
+}
+
struct testcase_t hs_service_tests[] = {
{ "e2e_rend_circuit_setup", test_e2e_rend_circuit_setup, TT_FORK,
NULL, NULL },
{ "load_keys", test_load_keys, TT_FORK,
NULL, NULL },
+ { "client_filename_is_valid", test_client_filename_is_valid, TT_FORK,
+ NULL, NULL },
+ { "parse_authorized_client", test_parse_authorized_client, TT_FORK,
+ NULL, NULL },
+ { "load_keys_with_client_auth", test_load_keys_with_client_auth, TT_FORK,
+ NULL, NULL },
{ "access_service", test_access_service, TT_FORK,
NULL, NULL },
{ "service_intro_point", test_service_intro_point, TT_FORK,
@@ -1583,10 +2038,14 @@ struct testcase_t hs_service_tests[] = {
NULL, NULL },
{ "build_update_descriptors", test_build_update_descriptors, TT_FORK,
NULL, NULL },
+ { "build_descriptors", test_build_descriptors, TT_FORK,
+ NULL, NULL },
{ "upload_descriptors", test_upload_descriptors, TT_FORK,
NULL, NULL },
{ "rendezvous1_parsing", test_rendezvous1_parsing, TT_FORK,
NULL, NULL },
+ { "authorized_client_config_equal", test_authorized_client_config_equal,
+ TT_FORK, NULL, NULL },
END_OF_TESTCASES
};