1 /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
5 * \file test_hs_control.c
6 * \brief Unit tests for hidden service control port event and command.
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
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
65 queue_control_event_string_replacement(uint16_t event
, char *msg
)
68 tor_free(received_msg
);
72 /** Mock function for node_describe_longname_by_id, it returns either
73 * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
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
;
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"
92 mock_node_get_by_id(const char *digest
)
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
);
102 test_hs_desc_event(void *arg
)
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
;
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
,
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 "
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",
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
);
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. */
211 test_hs_control_good_onion_client_auth_add(void *arg
)
215 MOCK(connection_write_to_buf_impl_
, connection_write_to_buf_mock
);
218 ed25519_public_key_t service_identity_pk_2fv
, service_identity_pk_jt4
,
219 service_identity_pk_jam
;
220 control_connection_t conn
;
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
,
238 tt_int_op(retval
, OP_EQ
, 0);
240 retval
= hs_parse_address(
241 "jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd",
242 &service_identity_pk_jt4
,
244 tt_int_op(retval
, OP_EQ
, 0);
246 retval
= hs_parse_address(
247 "jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd",
248 &service_identity_pk_jam
,
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);
264 cp1
= buf_get_contents(TO_CONN(&conn
)->outbuf
, &sz
);
265 tt_str_op(cp1
, OP_EQ
, "250 OK\r\n");
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);
278 cp1
= buf_get_contents(TO_CONN(&conn
)->outbuf
, &sz
);
279 tt_str_op(cp1
, OP_EQ
, "250 OK\r\n");
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);
292 cp1
= buf_get_contents(TO_CONN(&conn
)->outbuf
, &sz
);
293 tt_str_op(cp1
, OP_EQ
, "250 OK\r\n");
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 */
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" \
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
);
339 /* Now specify an HS addr, and see that we only view those creds */
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" \
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
);
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 */
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");
370 client_jt4
= digest256map_get(client_auths
, service_identity_pk_jt4
.pubkey
);
371 tt_assert(client_jt4
);
373 /* Now actually remove them. */
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");
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");
394 client_jt4
= digest256map_get(client_auths
, service_identity_pk_jt4
.pubkey
);
395 tt_assert(!client_jt4
);
397 /* Now also remove the other one */
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");
408 /* Now also remove the other one */
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");
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");
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
);
433 /* And a final VIEW with a wrong HS addr */
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");
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 */
452 test_hs_control_bad_onion_client_auth_add(void *arg
)
456 MOCK(connection_write_to_buf_impl_
, connection_write_to_buf_mock
);
459 control_connection_t conn
;
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 */
477 "badaddr x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=");
479 retval
= handle_control_command(&conn
, (uint32_t) strlen(args
), args
);
480 tt_int_op(retval
, OP_EQ
, 0);
483 cp1
= buf_get_contents(TO_CONN(&conn
)->outbuf
, &sz
);
484 tt_str_op(cp1
, OP_EQ
, "512 Invalid v3 address \"badaddr\"\r\n");
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);
497 cp1
= buf_get_contents(TO_CONN(&conn
)->outbuf
, &sz
);
498 tt_str_op(cp1
, OP_EQ
, "552 Unrecognized key type \"love\"\r\n");
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);
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");
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);
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
);
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
542 test_hs_control_store_permanent_creds(void *arg
)
546 MOCK(connection_write_to_buf_impl_
, connection_write_to_buf_mock
);
549 ed25519_public_key_t service_identity_pk_2fv
;
550 control_connection_t conn
;
553 char *creds_file_str
= NULL
;
554 char *creds_fname
= NULL
;
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
,
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= "
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 */
592 char *perm_creds_dir
= tor_strdup(get_fname("permanent_credentials"));
593 get_options_mutable()->ClientOnionAuthDir
= perm_creds_dir
;
596 ret
= mkdir(perm_creds_dir
);
598 ret
= mkdir(perm_creds_dir
, 0700);
600 tt_int_op(ret
, OP_EQ
, 0);
606 /* Try the control port command again. This time it should work! */
607 args
= tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
608 "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
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");
631 /* Overwrite the credentials and check that they got overwritten. */
632 args
= tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
633 "x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= "
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
);
673 /* Ensure that the creds file exists */
674 tt_int_op(file_status(creds_fname
), OP_EQ
, FN_FILE
);
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);
689 tor_free(get_options_mutable()->ClientOnionAuthDir
);
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
702 test_hs_control_add_onion_with_bad_pubkey(void *arg
)
706 MOCK(connection_write_to_buf_impl_
, connection_write_to_buf_mock
);
709 control_connection_t conn
;
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");
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. */
743 test_hs_control_add_auth_onion_service(void *arg
)
745 control_connection_t conn
;
746 char *args
= NULL
, *cp1
= NULL
;
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"
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");
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. */
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
;
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);
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
,
868 { "hs_control_good_onion_client_auth_add",
869 test_hs_control_good_onion_client_auth_add
, TT_FORK
,
871 { "hs_control_bad_onion_client_auth_add",
872 test_hs_control_bad_onion_client_auth_add
, TT_FORK
,
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
},