Merge branch 'maint-0.4.8' into release-0.4.8
[tor.git] / src / test / test_hs_control.c
blobeaeba47b0cfb910f7819fea73a91ce64a6e5742c
1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 /**
5 * \file test_hs_control.c
6 * \brief Unit tests for hidden service control port event and command.
7 **/
9 #define CONTROL_EVENTS_PRIVATE
10 #define CONTROL_CMD_PRIVATE
11 #define HS_CLIENT_PRIVATE
12 #define HS_SERVICE_PRIVATE
14 #include "core/or/or.h"
15 #include "test/test.h"
16 #include "test/test_helpers.h"
17 #include "core/mainloop/connection.h"
18 #include "feature/control/control.h"
19 #include "feature/control/control_cmd.h"
20 #include "feature/control/control_events.h"
21 #include "feature/control/control_fmt.h"
22 #include "feature/control/control_connection_st.h"
23 #include "app/config/config.h"
24 #include "feature/hs/hs_common.h"
25 #include "feature/hs/hs_client.h"
26 #include "feature/hs/hs_control.h"
27 #include "feature/nodelist/nodelist.h"
29 #include "feature/nodelist/node_st.h"
30 #include "feature/nodelist/routerstatus_st.h"
31 #include "lib/container/smartlist.h"
32 #include "lib/crypt_ops/crypto_format.h"
34 #ifdef HAVE_SYS_STAT_H
35 #include <sys/stat.h>
36 #endif
38 #ifdef _WIN32
39 /* For mkdir() */
40 #include <direct.h>
41 #else
42 #include <dirent.h>
43 #endif /* defined(_WIN32) */
45 /* mock ID digest and longname for node that's in nodelist */
46 #define HSDIR_EXIST_ID \
47 "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" \
48 "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
49 #define STR_HSDIR_EXIST_LONGNAME \
50 "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=TestDir"
51 #define STR_HSDIR_NONE_EXIST_LONGNAME \
52 "$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
54 /* Helper global variable for hidden service descriptor event test.
55 * It's used as a pointer to dynamically created message buffer in
56 * send_control_event_string_replacement function, which mocks
57 * send_control_event_string function.
59 * Always free it after use! */
60 static char *received_msg = NULL;
62 /** Mock function for send_control_event_string
64 static void
65 queue_control_event_string_replacement(uint16_t event, char *msg)
67 (void) event;
68 tor_free(received_msg);
69 received_msg = msg;
72 /** Mock function for node_describe_longname_by_id, it returns either
73 * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
75 static const char *
76 node_describe_longname_by_id_replacement(const char *id_digest)
78 if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
79 return STR_HSDIR_EXIST_LONGNAME;
80 } else {
81 return STR_HSDIR_NONE_EXIST_LONGNAME;
85 /* HSDir fetch index is a series of 'D' */
86 #define HSDIR_INDEX_FETCH_HEX \
87 "4343434343434343434343434343434343434343434343434343434343434343"
88 #define HSDIR_INDEX_STORE_HEX \
89 "4444444444444444444444444444444444444444444444444444444444444444"
91 static const node_t *
92 mock_node_get_by_id(const char *digest)
94 static node_t node;
95 memcpy(node.identity, digest, DIGEST_LEN);
96 memset(node.hsdir_index.fetch, 'C', DIGEST256_LEN);
97 memset(node.hsdir_index.store_first, 'D', DIGEST256_LEN);
98 return &node;
101 static void
102 test_hs_desc_event(void *arg)
104 int ret;
105 char *expected_msg = NULL;
106 char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
107 ed25519_keypair_t identity_kp;
108 ed25519_public_key_t blinded_pk;
109 char base64_blinded_pk[ED25519_BASE64_LEN + 1];
110 routerstatus_t hsdir_rs;
111 hs_ident_dir_conn_t ident;
113 (void) arg;
114 MOCK(queue_control_event_string,
115 queue_control_event_string_replacement);
116 MOCK(node_describe_longname_by_id,
117 node_describe_longname_by_id_replacement);
118 MOCK(node_get_by_id, mock_node_get_by_id);
120 /* Setup what we need for this test. */
121 ed25519_keypair_generate(&identity_kp, 0);
122 hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
123 ret = hs_address_is_valid(onion_address);
124 tt_int_op(ret, OP_EQ, 1);
125 memset(&blinded_pk, 'B', sizeof(blinded_pk));
126 memset(&hsdir_rs, 0, sizeof(hsdir_rs));
127 memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
128 ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
129 memcpy(&ident.identity_pk, &identity_kp.pubkey,
130 sizeof(ed25519_public_key_t));
131 memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
133 /* HS_DESC REQUESTED ... */
134 hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
135 &hsdir_rs);
136 tor_asprintf(&expected_msg, "650 HS_DESC REQUESTED %s NO_AUTH "
137 STR_HSDIR_EXIST_LONGNAME " %s HSDIR_INDEX="
138 HSDIR_INDEX_FETCH_HEX "\r\n",
139 onion_address, base64_blinded_pk);
140 tt_assert(received_msg);
141 tt_str_op(received_msg, OP_EQ, expected_msg);
142 tor_free(received_msg);
143 tor_free(expected_msg);
145 /* HS_DESC CREATED... */
146 hs_control_desc_event_created(onion_address, &blinded_pk);
147 tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
148 "UNKNOWN %s\r\n",
149 onion_address, base64_blinded_pk);
150 tt_assert(received_msg);
151 tt_str_op(received_msg, OP_EQ, expected_msg);
152 tor_free(received_msg);
153 tor_free(expected_msg);
155 /* HS_DESC UPLOAD... */
156 uint8_t hsdir_index_store[DIGEST256_LEN];
157 memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
158 hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
159 &blinded_pk, hsdir_index_store);
160 tor_asprintf(&expected_msg, "650 HS_DESC UPLOAD %s UNKNOWN "
161 STR_HSDIR_EXIST_LONGNAME " %s "
162 "HSDIR_INDEX=" HSDIR_INDEX_STORE_HEX "\r\n",
163 onion_address, base64_blinded_pk);
164 tt_assert(received_msg);
165 tt_str_op(received_msg, OP_EQ, expected_msg);
166 tor_free(received_msg);
167 tor_free(expected_msg);
169 /* HS_DESC FAILED... */
170 hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
171 tor_asprintf(&expected_msg, "650 HS_DESC FAILED %s NO_AUTH "
172 STR_HSDIR_EXIST_LONGNAME " %s "
173 "REASON=BAD_DESC\r\n",
174 onion_address, base64_blinded_pk);
175 tt_assert(received_msg);
176 tt_str_op(received_msg, OP_EQ, expected_msg);
177 tor_free(received_msg);
178 tor_free(expected_msg);
180 /* HS_DESC RECEIVED... */
181 hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
182 tor_asprintf(&expected_msg, "650 HS_DESC RECEIVED %s NO_AUTH "
183 STR_HSDIR_EXIST_LONGNAME " %s\r\n",
184 onion_address, base64_blinded_pk);
185 tt_assert(received_msg);
186 tt_str_op(received_msg, OP_EQ, expected_msg);
187 tor_free(received_msg);
188 tor_free(expected_msg);
190 /* HS_DESC UPLOADED... */
191 hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
192 tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
193 STR_HSDIR_EXIST_LONGNAME "\r\n",
194 onion_address);
195 tt_assert(received_msg);
196 tt_str_op(received_msg, OP_EQ, expected_msg);
197 tor_free(received_msg);
198 tor_free(expected_msg);
200 done:
201 UNMOCK(queue_control_event_string);
202 UNMOCK(node_describe_longname_by_id);
203 UNMOCK(node_get_by_id);
204 tor_free(received_msg);
205 tor_free(expected_msg);
208 /** Test that we can correctly add, remove and view client auth credentials
209 * using the control port. */
210 static void
211 test_hs_control_good_onion_client_auth_add(void *arg)
213 (void) arg;
215 MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
217 int retval;
218 ed25519_public_key_t service_identity_pk_2fv, service_identity_pk_jt4,
219 service_identity_pk_jam;
220 control_connection_t conn;
221 char *args = NULL;
222 char *cp1 = NULL;
223 size_t sz;
225 hs_init();
227 { /* Setup the control conn */
228 memset(&conn, 0, sizeof(control_connection_t));
229 TO_CONN(&conn)->outbuf = buf_new();
230 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
233 { /* Setup the services */
234 retval = hs_parse_address(
235 "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
236 &service_identity_pk_2fv,
237 NULL, NULL);
238 tt_int_op(retval, OP_EQ, 0);
240 retval = hs_parse_address(
241 "jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd",
242 &service_identity_pk_jt4,
243 NULL, NULL);
244 tt_int_op(retval, OP_EQ, 0);
246 retval = hs_parse_address(
247 "jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd",
248 &service_identity_pk_jam,
249 NULL, NULL);
250 tt_int_op(retval, OP_EQ, 0);
253 digest256map_t *client_auths = get_hs_client_auths_map();
254 tt_assert(!client_auths);
256 /* Register first service */
257 args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
258 "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= ");
260 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
261 tt_int_op(retval, OP_EQ, 0);
263 /* Check contents */
264 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
265 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
267 tor_free(cp1);
268 tor_free(args);
270 /* Register second service (even with an unrecognized argument) */
271 args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
272 "x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA= DropSound=No");
274 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
275 tt_int_op(retval, OP_EQ, 0);
277 /* Check contents */
278 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
279 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
280 tor_free(cp1);
281 tor_free(args);
283 /* Register second service (even with an unrecognized argument) */
284 args = tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd "
285 "x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= "
286 "ClientName=MeganNicole ");
288 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
289 tt_int_op(retval, OP_EQ, 0);
291 /* Check contents */
292 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
293 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
294 tor_free(cp1);
296 client_auths = get_hs_client_auths_map();
297 tt_assert(client_auths);
298 tt_uint_op(digest256map_size(client_auths), OP_EQ, 3);
300 hs_client_service_authorization_t *client_2fv =
301 digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
302 tt_assert(client_2fv);
303 tt_int_op(client_2fv->flags, OP_EQ, 0);
305 hs_client_service_authorization_t *client_jt4 =
306 digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
307 tt_assert(client_jt4);
308 tt_int_op(client_jt4->flags, OP_EQ, 0);
310 hs_client_service_authorization_t *client_jam =
311 digest256map_get(client_auths, service_identity_pk_jam.pubkey);
312 tt_assert(client_jam);
313 tt_int_op(client_jam->flags, OP_EQ, 0);
315 /* Now let's VIEW the auth credentials */
316 tor_free(conn.current_cmd);
317 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
319 /* First go with no arguments, so that we view all the credentials */
320 tor_free(args);
321 args = tor_strdup("");
323 #define VIEW_CORRECT_REPLY_NO_ADDR "250-ONION_CLIENT_AUTH_VIEW\r\n" \
324 "250-CLIENT 2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd " \
325 "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=\r\n" \
326 "250-CLIENT jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd " \
327 "x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= " \
328 "ClientName=MeganNicole\r\n" \
329 "250-CLIENT jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd " \
330 "x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=\r\n" \
331 "250 OK\r\n"
333 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
334 tt_int_op(retval, OP_EQ, 0);
335 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
336 tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NO_ADDR);
337 tor_free(cp1);
339 /* Now specify an HS addr, and see that we only view those creds */
340 tor_free(args);
341 args =
342 tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
344 #define VIEW_CORRECT_REPLY_JT4 "250-ONION_CLIENT_AUTH_VIEW " \
345 "jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd\r\n" \
346 "250-CLIENT jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd " \
347 "x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=\r\n" \
348 "250 OK\r\n"
350 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
351 tt_int_op(retval, OP_EQ, 0);
352 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
353 tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_JT4);
354 tor_free(cp1);
356 /* Now try to REMOVE the auth credentials */
357 tor_free(conn.current_cmd);
358 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
360 /* First try with a wrong addr */
361 tor_free(args);
362 args = tor_strdup("thatsok");
364 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
365 tt_int_op(retval, OP_EQ, 0);
366 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
367 tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"thatsok\"\r\n");
368 tor_free(cp1);
370 client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
371 tt_assert(client_jt4);
373 /* Now actually remove them. */
374 tor_free(args);
375 args =tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
377 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
378 tt_int_op(retval, OP_EQ, 0);
379 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
380 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
381 tor_free(cp1);
383 client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
384 tt_assert(!client_jt4);
386 /* Now try another time (we should get 'already removed' msg) */
387 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
388 tt_int_op(retval, OP_EQ, 0);
389 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
390 tt_str_op(cp1, OP_EQ, "251 No credentials for "
391 "\"jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd\"\r\n");
392 tor_free(cp1);
394 client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
395 tt_assert(!client_jt4);
397 /* Now also remove the other one */
398 tor_free(args);
399 args =
400 tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
402 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
403 tt_int_op(retval, OP_EQ, 0);
404 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
405 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
406 tor_free(cp1);
408 /* Now also remove the other one */
409 tor_free(args);
410 args =
411 tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd");
413 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
414 tt_int_op(retval, OP_EQ, 0);
415 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
416 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
417 tor_free(cp1);
419 /* Finally, do another VIEW and see that we get nothing. */
420 tor_free(conn.current_cmd);
421 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
422 tor_free(args);
423 args = tor_strdup("");
425 #define VIEW_CORRECT_REPLY_NOTHING "250-ONION_CLIENT_AUTH_VIEW\r\n250 OK\r\n"
427 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
428 tt_int_op(retval, OP_EQ, 0);
429 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
430 tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NOTHING);
431 tor_free(cp1);
433 /* And a final VIEW with a wrong HS addr */
434 tor_free(args);
435 args = tor_strdup("house");
437 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
438 tt_int_op(retval, OP_EQ, 0);
439 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
440 tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"house\"\r\n");
442 done:
443 tor_free(args);
444 tor_free(cp1);
445 buf_free(TO_CONN(&conn)->outbuf);
446 tor_free(conn.current_cmd);
447 hs_client_free_all();
450 /** Test some error cases of ONION_CLIENT_AUTH_ADD */
451 static void
452 test_hs_control_bad_onion_client_auth_add(void *arg)
454 (void) arg;
456 MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
458 int retval;
459 control_connection_t conn;
460 char *cp1 = NULL;
461 size_t sz;
462 char *args = NULL;
464 hs_init();
466 { /* Setup the control conn */
467 memset(&conn, 0, sizeof(control_connection_t));
468 TO_CONN(&conn)->outbuf = buf_new();
469 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
472 digest256map_t *client_auths = get_hs_client_auths_map();
473 tt_assert(!client_auths);
475 /* Register first service */
476 args = tor_strdup(
477 "badaddr x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=");
479 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
480 tt_int_op(retval, OP_EQ, 0);
482 /* Check contents */
483 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
484 tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"badaddr\"\r\n");
486 tor_free(cp1);
487 tor_free(args);
489 /* Register second service (even with an unrecognized argument) */
490 args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
491 "love:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=");
493 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
494 tt_int_op(retval, OP_EQ, 0);
496 /* Check contents */
497 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
498 tt_str_op(cp1, OP_EQ, "552 Unrecognized key type \"love\"\r\n");
500 tor_free(cp1);
501 tor_free(args);
503 /* Register second service (even with an unrecognized argument) */
504 args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
505 "x25519:QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEK");
507 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
508 tt_int_op(retval, OP_EQ, 0);
510 /* Check contents */
511 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
512 tt_str_op(cp1, OP_EQ, "512 Failed to decode x25519 private key\r\n");
514 tor_free(cp1);
515 tor_free(args);
517 /* Register with an all zero client key */
518 args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
519 "x25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
520 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
521 tt_int_op(retval, OP_EQ, 0);
523 /* Check contents */
524 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
525 tt_str_op(cp1, OP_EQ, "553 Invalid private key \"AAAAAAAAAAAAAAAAAAAA"
526 "AAAAAAAAAAAAAAAAAAAAAAA=\"\r\n");
528 client_auths = get_hs_client_auths_map();
529 tt_assert(!client_auths);
531 done:
532 tor_free(args);
533 tor_free(cp1);
534 buf_free(TO_CONN(&conn)->outbuf);
535 tor_free(conn.current_cmd);
536 hs_client_free_all();
539 /** Test that we can correctly add permanent client auth credentials using the
540 * control port. */
541 static void
542 test_hs_control_store_permanent_creds(void *arg)
544 (void) arg;
546 MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
548 int retval;
549 ed25519_public_key_t service_identity_pk_2fv;
550 control_connection_t conn;
551 char *args = NULL;
552 char *cp1 = NULL;
553 char *creds_file_str = NULL;
554 char *creds_fname = NULL;
556 size_t sz;
558 hs_init();
560 { /* Setup the control conn */
561 memset(&conn, 0, sizeof(control_connection_t));
562 TO_CONN(&conn)->outbuf = buf_new();
563 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
566 { /* Setup the services */
567 retval = hs_parse_address(
568 "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
569 &service_identity_pk_2fv,
570 NULL, NULL);
571 tt_int_op(retval, OP_EQ, 0);
574 digest256map_t *client_auths = get_hs_client_auths_map();
575 tt_assert(!client_auths);
577 /* Try registering first service with no ClientOnionAuthDir set */
578 args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
579 "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
580 "Flags=Permanent");
582 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
583 tt_int_op(retval, OP_EQ, 0);
585 /* Check control port response. This one should fail. */
586 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
587 tt_str_op(cp1, OP_EQ, "553 Unable to store creds for "
588 "\"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd\"\r\n");
590 { /* Setup ClientOnionAuthDir */
591 int ret;
592 char *perm_creds_dir = tor_strdup(get_fname("permanent_credentials"));
593 get_options_mutable()->ClientOnionAuthDir = perm_creds_dir;
595 #ifdef _WIN32
596 ret = mkdir(perm_creds_dir);
597 #else
598 ret = mkdir(perm_creds_dir, 0700);
599 #endif
600 tt_int_op(ret, OP_EQ, 0);
603 tor_free(args);
604 tor_free(cp1);
606 /* Try the control port command again. This time it should work! */
607 args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
608 "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
609 "Flags=Permanent");
610 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
611 tt_int_op(retval, OP_EQ, 0);
613 /* Check control port response */
614 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
615 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
617 /* Check file contents! */
618 creds_fname = tor_strdup(get_fname("permanent_credentials/"
619 "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd.auth_private"));
620 creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
622 tt_assert(creds_file_str);
623 tt_str_op(creds_file_str, OP_EQ,
624 "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
625 /* base32 representation of the base64 iJ1t... key above */
626 "x25519:rcow3dfavmyanyqvhwnvnmfdqw34ydtrgv7jnelmqs4wi4uuxrca");
628 tor_free(args);
629 tor_free(cp1);
631 /* Overwrite the credentials and check that they got overwritten. */
632 args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
633 "x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= "
634 "Flags=Permanent");
635 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
636 tt_int_op(retval, OP_EQ, 0);
638 /* Check control port response: we replaced! */
639 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
640 tt_str_op(cp1, OP_EQ, "251 Client for onion existed and replaced\r\n");
642 tor_free(creds_file_str);
644 /* Check creds file contents again. See that the key got updated */
645 creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
646 tt_assert(creds_file_str);
647 tt_str_op(creds_file_str, OP_EQ,
648 "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
649 /* base32 representation of the base64 UDRv... key above */
650 "x25519:ka2g6zf33qti2ecexpbx4stan3nsu3sijbiqm4t2rwctigxajnpq");
652 /* Now for our next act!!! Actually get the HS client subsystem to parse the
653 * whole directory and make sure that it extracted the right credential! */
654 hs_config_client_authorization(get_options(), 0);
656 client_auths = get_hs_client_auths_map();
657 tt_assert(client_auths);
658 tt_uint_op(digest256map_size(client_auths), OP_EQ, 1);
660 hs_client_service_authorization_t *client_2fv =
661 digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
662 tt_assert(client_2fv);
663 tt_int_op(client_2fv->flags, OP_EQ, CLIENT_AUTH_FLAG_IS_PERMANENT);
664 tt_str_op(hex_str((char*)client_2fv->enc_seckey.secret_key, 32), OP_EQ,
665 "50346F64BBDC268D1044BBC37E4A606EDB2A6E48485106727A8D85341AE04B5F");
667 /* And now for the final act! Use the REMOVE control port command to remove
668 the credential, and ensure that the file has also been removed! */
669 tor_free(conn.current_cmd);
670 tor_free(cp1);
671 tor_free(args);
673 /* Ensure that the creds file exists */
674 tt_int_op(file_status(creds_fname), OP_EQ, FN_FILE);
676 /* Do the REMOVE */
677 conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
678 args =tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
679 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
680 tt_int_op(retval, OP_EQ, 0);
681 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
682 tt_str_op(cp1, OP_EQ, "250 OK\r\n");
684 /* Ensure that the file has been removed and the map is empty */
685 tt_int_op(file_status(creds_fname), OP_EQ, FN_NOENT);
686 tt_uint_op(digest256map_size(client_auths), OP_EQ, 0);
688 done:
689 tor_free(get_options_mutable()->ClientOnionAuthDir);
690 tor_free(args);
691 tor_free(cp1);
692 buf_free(TO_CONN(&conn)->outbuf);
693 tor_free(conn.current_cmd);
694 tor_free(creds_fname);
695 tor_free(creds_file_str);
696 hs_client_free_all();
699 /** Test that ADD_ONION properly handles an attacker passing it a bad private
700 * key. */
701 static void
702 test_hs_control_add_onion_with_bad_pubkey(void *arg)
704 (void) arg;
706 MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
708 int retval;
709 control_connection_t conn;
710 char *args = NULL;
711 char *cp1 = NULL;
712 size_t sz;
714 hs_init();
716 { /* Setup the control conn */
717 memset(&conn, 0, sizeof(control_connection_t));
718 TO_CONN(&conn)->outbuf = buf_new();
719 conn.current_cmd = tor_strdup("ADD_ONION");
722 args = tor_strdup("ED25519-V3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
723 "AAAAAAAAAAAAAAAAAAAAAAA"
724 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
725 "Port=9735,127.0.0.1 Flags=DiscardPK");
727 retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
728 tt_int_op(retval, OP_EQ, 0);
730 /* Check control port response */
731 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
732 tt_str_op(cp1, OP_EQ, "551 Failed to generate onion address\r\n");
734 done:
735 tor_free(args);
736 tor_free(cp1);
737 buf_free(TO_CONN(&conn)->outbuf);
738 tor_free(conn.current_cmd);
741 /** Test that we can add the service via the control port. */
742 static void
743 test_hs_control_add_auth_onion_service(void *arg)
745 control_connection_t conn;
746 char *args = NULL, *cp1 = NULL;
747 size_t sz;
749 (void) arg;
751 hs_init();
753 memset(&conn, 0, sizeof(control_connection_t));
754 TO_CONN(&conn)->outbuf = buf_new();
755 conn.current_cmd = tor_strdup("ADD_ONION");
756 args = tor_strdup("ED25519-V3:KLMQ4CLKwlDCHuMPn8j3od33cU5LhnrLNoZh7CWChl3VkY"
757 "pNAkeP5dGW8xeKR9HxQBWQ/w7Kr12lA/U8Pd/oxw== "
758 "ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja "
759 "Flags=V3Auth Port=9735,127.0.0.1");
760 handle_control_command(&conn, (uint32_t) strlen(args), args);
761 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
762 tt_str_op(cp1, OP_EQ,
763 "250-ServiceID=n35etu3yjxrqjpntmfziom5sjwspoydchmelc4xleoy4jk2u4lziz2yd\r\n"
764 "250-ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja\r\n"
765 "250 OK\r\n");
766 tor_free(args);
767 tor_free(cp1);
769 args = tor_strdup("ED25519-V3:iIU8EBi71qE7G6UTsROU1kWN0JMrRP/YukC0Xk5WLGyil3"
770 "gm4u3wEBXr+/TaCpXS+65Pcdqz+PG+4+oWHLN05A== "
771 "ClientAuthV3=dummy Flags=V3Auth Port=9735,127.0.0.1");
772 handle_control_command(&conn, (uint32_t) strlen(args), args);
773 cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
774 tt_str_op(cp1, OP_EQ, "512 Cannot decode v3 client auth key\r\n");
776 done:
777 tor_free(args);
778 tor_free(cp1);
779 tor_free(conn.current_cmd);
780 buf_free(TO_CONN(&conn)->outbuf);
781 SMARTLIST_FOREACH(conn.ephemeral_onion_services, char *,
782 service, tor_free(service));
783 smartlist_free(conn.ephemeral_onion_services);
784 hs_client_free_all();
787 /** Test that add_onion_helper_add_service can add the service. */
788 static void
789 test_hs_control_add_onion_helper_add_service(void *arg)
791 int hs_version_good, hs_version_bad;
792 add_onion_secret_key_t sk_good, sk_bad;
793 ed25519_public_key_t pk_good, pk_bad;
794 char *key_new_blob_good = NULL, *key_new_blob_bad = NULL;
795 const char *key_new_alg_good = NULL, *key_new_alg_bad = NULL;
796 hs_service_authorized_client_t *client_good, *client_bad;
797 smartlist_t *list_good, *list_bad;
798 hs_service_ht *global_map;
799 hs_port_config_t *portcfg;
800 smartlist_t *portcfgs;
801 char *address_out_good = NULL, *address_out_bad = NULL;
802 hs_service_t *service_good = NULL;
803 hs_service_t *service_bad = NULL;
805 (void) arg;
807 hs_init();
808 global_map = get_hs_service_map();
810 portcfg = hs_parse_port_config("8080", ",", NULL);
811 portcfgs = smartlist_new();
812 smartlist_add(portcfgs, portcfg);
814 memset(&sk_good, 0, sizeof(sk_good));
815 memset(&sk_bad, 0, sizeof(sk_bad));
817 add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_good,
818 &key_new_blob_good, &sk_good, &hs_version_good, NULL);
819 add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_bad,
820 &key_new_blob_bad, &sk_bad, &hs_version_bad, NULL);
822 ed25519_public_key_generate(&pk_good, sk_good.v3);
823 ed25519_public_key_generate(&pk_bad, sk_bad.v3);
825 client_good = parse_authorized_client_key(
826 "N2NU7BSRL6YODZCYPN4CREB54TYLKGIE2KYOQWLFYC23ZJVCE5DQ", LOG_INFO);
827 client_bad = parse_authorized_client_key("dummy", LOG_INFO);
829 list_good = smartlist_new();
830 smartlist_add(list_good, client_good);
832 add_onion_helper_add_service(HS_VERSION_THREE, &sk_good, portcfgs, 1, 1,
833 list_good, &address_out_good);
835 service_good = find_service(global_map, &pk_good);
836 tt_int_op(smartlist_len(service_good->config.clients), OP_EQ, 1);
838 remove_service(global_map, service_good);
839 hs_service_free(service_good);
841 list_bad = smartlist_new();
842 smartlist_add(list_bad, client_bad);
844 portcfg = hs_parse_port_config("8080", ",", NULL);
845 portcfgs = smartlist_new();
846 smartlist_add(portcfgs, portcfg);
848 add_onion_helper_add_service(HS_VERSION_THREE, &sk_bad, portcfgs, 1, 1,
849 list_bad, &address_out_bad);
851 service_bad = find_service(global_map, &pk_bad);
853 tt_int_op(smartlist_len(service_bad->config.clients), OP_EQ, 0);
855 done:
856 tor_free(key_new_blob_good);
857 tor_free(key_new_blob_bad);
858 tor_free(address_out_good);
859 tor_free(address_out_bad);
861 hs_service_free(service_good);
862 hs_service_free(service_bad);
865 struct testcase_t hs_control_tests[] = {
866 { "hs_desc_event", test_hs_desc_event, TT_FORK,
867 NULL, NULL },
868 { "hs_control_good_onion_client_auth_add",
869 test_hs_control_good_onion_client_auth_add, TT_FORK,
870 NULL, NULL },
871 { "hs_control_bad_onion_client_auth_add",
872 test_hs_control_bad_onion_client_auth_add, TT_FORK,
873 NULL, NULL },
874 { "hs_control_store_permanent_creds",
875 test_hs_control_store_permanent_creds, TT_FORK, NULL, NULL },
876 { "hs_control_add_onion_with_bad_pubkey",
877 test_hs_control_add_onion_with_bad_pubkey, TT_FORK, NULL, NULL },
878 { "hs_control_add_auth_onion_service",
879 test_hs_control_add_auth_onion_service, TT_FORK, NULL, NULL},
880 { "hs_control_add_onion_helper_add_service",
881 test_hs_control_add_onion_helper_add_service, TT_FORK, NULL, NULL},
883 END_OF_TESTCASES