tor/src/test/test_hs_control.c
Nick Mathewson 1d9de2948b test_hs_control: initialize pointers to NULL
This is an attempt to fix CID 1486276, where coverity warns us that,
if the tests failed, we might free() an uninitialized pointer.
2021-07-12 14:59:29 -04:00

884 lines
30 KiB
C

/* Copyright (c) 2017-2021, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file test_hs_control.c
* \brief Unit tests for hidden service control port event and command.
**/
#define CONTROL_EVENTS_PRIVATE
#define CONTROL_CMD_PRIVATE
#define HS_CLIENT_PRIVATE
#define HS_SERVICE_PRIVATE
#include "core/or/or.h"
#include "test/test.h"
#include "test/test_helpers.h"
#include "core/mainloop/connection.h"
#include "feature/control/control.h"
#include "feature/control/control_cmd.h"
#include "feature/control/control_events.h"
#include "feature/control/control_fmt.h"
#include "feature/control/control_connection_st.h"
#include "app/config/config.h"
#include "feature/hs/hs_common.h"
#include "feature/hs/hs_client.h"
#include "feature/hs/hs_control.h"
#include "feature/nodelist/nodelist.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerstatus_st.h"
#include "lib/container/smartlist.h"
#include "lib/crypt_ops/crypto_format.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef _WIN32
/* For mkdir() */
#include <direct.h>
#else
#include <dirent.h>
#endif /* defined(_WIN32) */
/* mock ID digest and longname for node that's in nodelist */
#define HSDIR_EXIST_ID \
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
"\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
#define STR_HSDIR_EXIST_LONGNAME \
"$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
#define STR_HSDIR_NONE_EXIST_LONGNAME \
"$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
/* Helper global variable for hidden service descriptor event test.
* It's used as a pointer to dynamically created message buffer in
* send_control_event_string_replacement function, which mocks
* send_control_event_string function.
*
* Always free it after use! */
static char *received_msg = NULL;
/** Mock function for send_control_event_string
*/
static void
queue_control_event_string_replacement(uint16_t event, char *msg)
{
(void) event;
tor_free(received_msg);
received_msg = msg;
}
/** Mock function for node_describe_longname_by_id, it returns either
* STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
*/
static const char *
node_describe_longname_by_id_replacement(const char *id_digest)
{
if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
return STR_HSDIR_EXIST_LONGNAME;
} else {
return STR_HSDIR_NONE_EXIST_LONGNAME;
}
}
/* HSDir fetch index is a series of 'D' */
#define HSDIR_INDEX_FETCH_HEX \
"4343434343434343434343434343434343434343434343434343434343434343"
#define HSDIR_INDEX_STORE_HEX \
"4444444444444444444444444444444444444444444444444444444444444444"
static const node_t *
mock_node_get_by_id(const char *digest)
{
static node_t node;
memcpy(node.identity, digest, DIGEST_LEN);
memset(node.hsdir_index.fetch, 'C', DIGEST256_LEN);
memset(node.hsdir_index.store_first, 'D', DIGEST256_LEN);
return &node;
}
static void
test_hs_desc_event(void *arg)
{
int ret;
char *expected_msg = NULL;
char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
ed25519_keypair_t identity_kp;
ed25519_public_key_t blinded_pk;
char base64_blinded_pk[ED25519_BASE64_LEN + 1];
routerstatus_t hsdir_rs;
hs_ident_dir_conn_t ident;
(void) arg;
MOCK(queue_control_event_string,
queue_control_event_string_replacement);
MOCK(node_describe_longname_by_id,
node_describe_longname_by_id_replacement);
MOCK(node_get_by_id, mock_node_get_by_id);
/* Setup what we need for this test. */
ed25519_keypair_generate(&identity_kp, 0);
hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
ret = hs_address_is_valid(onion_address);
tt_int_op(ret, OP_EQ, 1);
memset(&blinded_pk, 'B', sizeof(blinded_pk));
memset(&hsdir_rs, 0, sizeof(hsdir_rs));
memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
memcpy(&ident.identity_pk, &identity_kp.pubkey,
sizeof(ed25519_public_key_t));
memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
/* HS_DESC REQUESTED ... */
hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
&hsdir_rs);
tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH "
STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX="
HSDIR_INDEX_FETCH_HEX "\r\n",
onion_address, base64_blinded_pk);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
/* HS_DESC CREATED... */
hs_control_desc_event_created(onion_address, &blinded_pk);
tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
"UNKNOWN %s\r\n",
onion_address, base64_blinded_pk);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
/* HS_DESC UPLOAD... */
uint8_t hsdir_index_store[DIGEST256_LEN];
memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
&blinded_pk, hsdir_index_store);
tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN "
STR_HSDIR_EXIST_LONGNAME " %s "
"HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n",
onion_address, base64_blinded_pk);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
/* HS_DESC FAILED... */
hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH "
STR_HSDIR_EXIST_LONGNAME " %s "
"REASON=BAD_DESC\r\n",
onion_address, base64_blinded_pk);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
/* HS_DESC RECEIVED... */
hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH "
STR_HSDIR_EXIST_LONGNAME " %s\r\n",
onion_address, base64_blinded_pk);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
/* HS_DESC UPLOADED... */
hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
STR_HSDIR_EXIST_LONGNAME "\r\n",
onion_address);
tt_assert(received_msg);
tt_str_op(received_msg, OP_EQ, expected_msg);
tor_free(received_msg);
tor_free(expected_msg);
done:
UNMOCK(queue_control_event_string);
UNMOCK(node_describe_longname_by_id);
UNMOCK(node_get_by_id);
tor_free(received_msg);
tor_free(expected_msg);
}
/** Test that we can correctly add, remove and view client auth credentials
* using the control port. */
static void
test_hs_control_good_onion_client_auth_add(void *arg)
{
(void) arg;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
int retval;
ed25519_public_key_t service_identity_pk_2fv, service_identity_pk_jt4,
service_identity_pk_jam;
control_connection_t conn;
char *args = NULL;
char *cp1 = NULL;
size_t sz;
hs_init();
{ /* Setup the control conn */
memset(&conn, 0, sizeof(control_connection_t));
TO_CONN(&conn)->outbuf = buf_new();
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
}
{ /* Setup the services */
retval = hs_parse_address(
"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
&service_identity_pk_2fv,
NULL, NULL);
tt_int_op(retval, OP_EQ, 0);
retval = hs_parse_address(
"jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd",
&service_identity_pk_jt4,
NULL, NULL);
tt_int_op(retval, OP_EQ, 0);
retval = hs_parse_address(
"jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd",
&service_identity_pk_jam,
NULL, NULL);
tt_int_op(retval, OP_EQ, 0);
}
digest256map_t *client_auths = get_hs_client_auths_map();
tt_assert(!client_auths);
/* Register first service */
args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
"x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= ");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
tor_free(args);
/* Register second service (even with an unrecognized argument) */
args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
"x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA= DropSound=No");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
tor_free(args);
/* Register second service (even with an unrecognized argument) */
args = tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd "
"x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= "
"ClientName=MeganNicole ");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
client_auths = get_hs_client_auths_map();
tt_assert(client_auths);
tt_uint_op(digest256map_size(client_auths), OP_EQ, 3);
hs_client_service_authorization_t *client_2fv =
digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
tt_assert(client_2fv);
tt_int_op(client_2fv->flags, OP_EQ, 0);
hs_client_service_authorization_t *client_jt4 =
digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
tt_assert(client_jt4);
tt_int_op(client_jt4->flags, OP_EQ, 0);
hs_client_service_authorization_t *client_jam =
digest256map_get(client_auths, service_identity_pk_jam.pubkey);
tt_assert(client_jam);
tt_int_op(client_jam->flags, OP_EQ, 0);
/* Now let's VIEW the auth credentials */
tor_free(conn.current_cmd);
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
/* First go with no arguments, so that we view all the credentials */
tor_free(args);
args = tor_strdup("");
#define VIEW_CORRECT_REPLY_NO_ADDR "250-ONION_CLIENT_AUTH_VIEW\r\n" \
"250-CLIENT 2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd " \
"x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=\r\n" \
"250-CLIENT jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd " \
"x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= " \
"ClientName=MeganNicole\r\n" \
"250-CLIENT jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd " \
"x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=\r\n" \
"250 OK\r\n"
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NO_ADDR);
tor_free(cp1);
/* Now specify an HS addr, and see that we only view those creds */
tor_free(args);
args =
tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
#define VIEW_CORRECT_REPLY_JT4 "250-ONION_CLIENT_AUTH_VIEW " \
"jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd\r\n" \
"250-CLIENT jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd " \
"x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=\r\n" \
"250 OK\r\n"
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_JT4);
tor_free(cp1);
/* Now try to REMOVE the auth credentials */
tor_free(conn.current_cmd);
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
/* First try with a wrong addr */
tor_free(args);
args = tor_strdup("thatsok");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"thatsok\"\r\n");
tor_free(cp1);
client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
tt_assert(client_jt4);
/* Now actually remove them. */
tor_free(args);
args =tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
tt_assert(!client_jt4);
/* Now try another time (we should get 'already removed' msg) */
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "251 No credentials for "
"\"jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd\"\r\n");
tor_free(cp1);
client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
tt_assert(!client_jt4);
/* Now also remove the other one */
tor_free(args);
args =
tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
/* Now also remove the other one */
tor_free(args);
args =
tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
tor_free(cp1);
/* Finally, do another VIEW and see that we get nothing. */
tor_free(conn.current_cmd);
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
tor_free(args);
args = tor_strdup("");
#define VIEW_CORRECT_REPLY_NOTHING "250-ONION_CLIENT_AUTH_VIEW\r\n250 OK\r\n"
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NOTHING);
tor_free(cp1);
/* And a final VIEW with a wrong HS addr */
tor_free(args);
args = tor_strdup("house");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"house\"\r\n");
done:
tor_free(args);
tor_free(cp1);
buf_free(TO_CONN(&conn)->outbuf);
tor_free(conn.current_cmd);
hs_client_free_all();
}
/** Test some error cases of ONION_CLIENT_AUTH_ADD */
static void
test_hs_control_bad_onion_client_auth_add(void *arg)
{
(void) arg;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
int retval;
control_connection_t conn;
char *cp1 = NULL;
size_t sz;
char *args = NULL;
hs_init();
{ /* Setup the control conn */
memset(&conn, 0, sizeof(control_connection_t));
TO_CONN(&conn)->outbuf = buf_new();
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
}
digest256map_t *client_auths = get_hs_client_auths_map();
tt_assert(!client_auths);
/* Register first service */
args = tor_strdup(
"badaddr x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"badaddr\"\r\n");
tor_free(cp1);
tor_free(args);
/* Register second service (even with an unrecognized argument) */
args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
"love:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "552 Unrecognized key type \"love\"\r\n");
tor_free(cp1);
tor_free(args);
/* Register second service (even with an unrecognized argument) */
args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
"x25519:QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEK");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "512 Failed to decode x25519 private key\r\n");
tor_free(cp1);
tor_free(args);
/* Register with an all zero client key */
args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
"x25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check contents */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "553 Invalid private key \"AAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAA=\"\r\n");
client_auths = get_hs_client_auths_map();
tt_assert(!client_auths);
done:
tor_free(args);
tor_free(cp1);
buf_free(TO_CONN(&conn)->outbuf);
tor_free(conn.current_cmd);
hs_client_free_all();
}
/** Test that we can correctly add permanent client auth credentials using the
* control port. */
static void
test_hs_control_store_permanent_creds(void *arg)
{
(void) arg;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
int retval;
ed25519_public_key_t service_identity_pk_2fv;
control_connection_t conn;
char *args = NULL;
char *cp1 = NULL;
char *creds_file_str = NULL;
char *creds_fname = NULL;
size_t sz;
hs_init();
{ /* Setup the control conn */
memset(&conn, 0, sizeof(control_connection_t));
TO_CONN(&conn)->outbuf = buf_new();
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
}
{ /* Setup the services */
retval = hs_parse_address(
"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
&service_identity_pk_2fv,
NULL, NULL);
tt_int_op(retval, OP_EQ, 0);
}
digest256map_t *client_auths = get_hs_client_auths_map();
tt_assert(!client_auths);
/* Try registering first service with no ClientOnionAuthDir set */
args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
"x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
"Flags=Permanent");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check control port response. This one should fail. */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "553 Unable to store creds for "
"\"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd\"\r\n");
{ /* Setup ClientOnionAuthDir */
int ret;
char *perm_creds_dir = tor_strdup(get_fname("permanent_credentials"));
get_options_mutable()->ClientOnionAuthDir = perm_creds_dir;
#ifdef _WIN32
ret = mkdir(perm_creds_dir);
#else
ret = mkdir(perm_creds_dir, 0700);
#endif
tt_int_op(ret, OP_EQ, 0);
}
tor_free(args);
tor_free(cp1);
/* Try the control port command again. This time it should work! */
args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
"x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
"Flags=Permanent");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check control port response */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
/* Check file contents! */
creds_fname = tor_strdup(get_fname("permanent_credentials/"
"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd.auth_private"));
creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
tt_assert(creds_file_str);
tt_str_op(creds_file_str, OP_EQ,
"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
/* base32 representation of the base64 iJ1t... key above */
"x25519:rcow3dfavmyanyqvhwnvnmfdqw34ydtrgv7jnelmqs4wi4uuxrca");
tor_free(args);
tor_free(cp1);
/* Overwrite the credentials and check that they got overwrited. */
args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
"x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= "
"Flags=Permanent");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check control port response: we replaced! */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "251 Client for onion existed and replaced\r\n");
tor_free(creds_file_str);
/* Check creds file contents again. See that the key got updated */
creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
tt_assert(creds_file_str);
tt_str_op(creds_file_str, OP_EQ,
"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
/* base32 representation of the base64 UDRv... key above */
"x25519:ka2g6zf33qti2ecexpbx4stan3nsu3sijbiqm4t2rwctigxajnpq");
/* Now for our next act!!! Actually get the HS client subsystem to parse the
* whole directory and make sure that it extracted the right credential! */
hs_config_client_authorization(get_options(), 0);
client_auths = get_hs_client_auths_map();
tt_assert(client_auths);
tt_uint_op(digest256map_size(client_auths), OP_EQ, 1);
hs_client_service_authorization_t *client_2fv =
digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
tt_assert(client_2fv);
tt_int_op(client_2fv->flags, OP_EQ, CLIENT_AUTH_FLAG_IS_PERMANENT);
tt_str_op(hex_str((char*)client_2fv->enc_seckey.secret_key, 32), OP_EQ,
"50346F64BBDC268D1044BBC37E4A606EDB2A6E48485106727A8D85341AE04B5F");
/* And now for the final act! Use the REMOVE control port command to remove
the credential, and ensure that the file has also been removed! */
tor_free(conn.current_cmd);
tor_free(cp1);
tor_free(args);
/* Ensure that the creds file exists */
tt_int_op(file_status(creds_fname), OP_EQ, FN_FILE);
/* Do the REMOVE */
conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
args =tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "250 OK\r\n");
/* Ensure that the file has been removed and the map is empty */
tt_int_op(file_status(creds_fname), OP_EQ, FN_NOENT);
tt_uint_op(digest256map_size(client_auths), OP_EQ, 0);
done:
tor_free(get_options_mutable()->ClientOnionAuthDir);
tor_free(args);
tor_free(cp1);
buf_free(TO_CONN(&conn)->outbuf);
tor_free(conn.current_cmd);
tor_free(creds_fname);
tor_free(creds_file_str);
hs_client_free_all();
}
/** Test that ADD_ONION properly handles an attacker passing it a bad private
* key. */
static void
test_hs_control_add_onion_with_bad_pubkey(void *arg)
{
(void) arg;
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
int retval;
control_connection_t conn;
char *args = NULL;
char *cp1 = NULL;
size_t sz;
hs_init();
{ /* Setup the control conn */
memset(&conn, 0, sizeof(control_connection_t));
TO_CONN(&conn)->outbuf = buf_new();
conn.current_cmd = tor_strdup("ADD_ONION");
}
args = tor_strdup("ED25519-V3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
"Port=9735,127.0.0.1 Flags=DiscardPK");
retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
tt_int_op(retval, OP_EQ, 0);
/* Check control port response */
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "551 Failed to generate onion address\r\n");
done:
tor_free(args);
tor_free(cp1);
buf_free(TO_CONN(&conn)->outbuf);
tor_free(conn.current_cmd);
}
/** Test that we can add the service via the control port. */
static void
test_hs_control_add_auth_onion_service(void *arg)
{
control_connection_t conn;
char *args = NULL, *cp1 = NULL;
size_t sz;
(void) arg;
hs_init();
memset(&conn, 0, sizeof(control_connection_t));
TO_CONN(&conn)->outbuf = buf_new();
conn.current_cmd = tor_strdup("ADD_ONION");
args = tor_strdup("ED25519-V3:KLMQ4CLKwlDCHuMPn8j3od33cU5LhnrLNoZh7CWChl3VkY"
"pNAkeP5dGW8xeKR9HxQBWQ/w7Kr12lA/U8Pd/oxw== "
"ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja "
"Flags=V3Auth Port=9735,127.0.0.1");
handle_control_command(&conn, (uint32_t) strlen(args), args);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ,
"250-ServiceID=n35etu3yjxrqjpntmfziom5sjwspoydchmelc4xleoy4jk2u4lziz2yd\r\n"
"250-ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja\r\n"
"250 OK\r\n");
tor_free(args);
tor_free(cp1);
args = tor_strdup("ED25519-V3:iIU8EBi71qE7G6UTsROU1kWN0JMrRP/YukC0Xk5WLGyil3"
"gm4u3wEBXr+/TaCpXS+65Pcdqz+PG+4+oWHLN05A== "
"ClientAuthV3=dummy Flags=V3Auth Port=9735,127.0.0.1");
handle_control_command(&conn, (uint32_t) strlen(args), args);
cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
tt_str_op(cp1, OP_EQ, "512 Cannot decode v3 client auth key\r\n");
done:
tor_free(args);
tor_free(cp1);
tor_free(conn.current_cmd);
buf_free(TO_CONN(&conn)->outbuf);
SMARTLIST_FOREACH(conn.ephemeral_onion_services, char *,
service, tor_free(service));
smartlist_free(conn.ephemeral_onion_services);
hs_client_free_all();
}
/** Test that add_onion_helper_add_service can add the service. */
static void
test_hs_control_add_onion_helper_add_service(void *arg)
{
int hs_version_good, hs_version_bad;
add_onion_secret_key_t sk_good, sk_bad;
ed25519_public_key_t pk_good, pk_bad;
char *key_new_blob_good = NULL, *key_new_blob_bad = NULL;
const char *key_new_alg_good = NULL, *key_new_alg_bad = NULL;
hs_service_authorized_client_t *client_good, *client_bad;
smartlist_t *list_good, *list_bad;
hs_service_ht *global_map;
hs_port_config_t *portcfg;
smartlist_t *portcfgs;
char *address_out_good = NULL, *address_out_bad = NULL;
hs_service_t *service_good = NULL;
hs_service_t *service_bad = NULL;
(void) arg;
hs_init();
global_map = get_hs_service_map();
portcfg = hs_parse_port_config("8080", ",", NULL);
portcfgs = smartlist_new();
smartlist_add(portcfgs, portcfg);
memset(&sk_good, 0, sizeof(sk_good));
memset(&sk_bad, 0, sizeof(sk_bad));
add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_good,
&key_new_blob_good, &sk_good, &hs_version_good, NULL);
add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_bad,
&key_new_blob_bad, &sk_bad, &hs_version_bad, NULL);
ed25519_public_key_generate(&pk_good, sk_good.v3);
ed25519_public_key_generate(&pk_bad, sk_bad.v3);
client_good = parse_authorized_client_key(
"N2NU7BSRL6YODZCYPN4CREB54TYLKGIE2KYOQWLFYC23ZJVCE5DQ", LOG_INFO);
client_bad = parse_authorized_client_key("dummy", LOG_INFO);
list_good = smartlist_new();
smartlist_add(list_good, client_good);
add_onion_helper_add_service(HS_VERSION_THREE, &sk_good, portcfgs, 1, 1,
list_good, &address_out_good);
service_good = find_service(global_map, &pk_good);
tt_int_op(smartlist_len(service_good->config.clients), OP_EQ, 1);
remove_service(global_map, service_good);
hs_service_free(service_good);
list_bad = smartlist_new();
smartlist_add(list_bad, client_bad);
portcfg = hs_parse_port_config("8080", ",", NULL);
portcfgs = smartlist_new();
smartlist_add(portcfgs, portcfg);
add_onion_helper_add_service(HS_VERSION_THREE, &sk_bad, portcfgs, 1, 1,
list_bad, &address_out_bad);
service_bad = find_service(global_map, &pk_bad);
tt_int_op(smartlist_len(service_bad->config.clients), OP_EQ, 0);
done:
tor_free(key_new_blob_good);
tor_free(key_new_blob_bad);
tor_free(address_out_good);
tor_free(address_out_bad);
hs_service_free(service_good);
hs_service_free(service_bad);
}
struct testcase_t hs_control_tests[] = {
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
NULL, NULL },
{ "hs_control_good_onion_client_auth_add",
test_hs_control_good_onion_client_auth_add, TT_FORK,
NULL, NULL },
{ "hs_control_bad_onion_client_auth_add",
test_hs_control_bad_onion_client_auth_add, TT_FORK,
NULL, NULL },
{ "hs_control_store_permanent_creds",
test_hs_control_store_permanent_creds, TT_FORK, NULL, NULL },
{ "hs_control_add_onion_with_bad_pubkey",
test_hs_control_add_onion_with_bad_pubkey, TT_FORK, NULL, NULL },
{ "hs_control_add_auth_onion_service",
test_hs_control_add_auth_onion_service, TT_FORK, NULL, NULL},
{ "hs_control_add_onion_helper_add_service",
test_hs_control_add_onion_helper_add_service, TT_FORK, NULL, NULL},
END_OF_TESTCASES
};