Line data Source code
1 : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file test_hs_control.c
6 : * \brief Unit tests for hidden service control port event and command.
7 : **/
8 :
9 : #define CONTROL_EVENTS_PRIVATE
10 : #define CONTROL_CMD_PRIVATE
11 : #define HS_CLIENT_PRIVATE
12 : #define HS_SERVICE_PRIVATE
13 :
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"
28 :
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"
33 :
34 : #ifdef HAVE_SYS_STAT_H
35 : #include <sys/stat.h>
36 : #endif
37 :
38 : #ifdef _WIN32
39 : /* For mkdir() */
40 : #include <direct.h>
41 : #else
42 : #include <dirent.h>
43 : #endif /* defined(_WIN32) */
44 :
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"
53 :
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.
58 : *
59 : * Always free it after use! */
60 : static char *received_msg = NULL;
61 :
62 : /** Mock function for send_control_event_string
63 : */
64 : static void
65 6 : queue_control_event_string_replacement(uint16_t event, char *msg)
66 : {
67 6 : (void) event;
68 6 : tor_free(received_msg);
69 6 : received_msg = msg;
70 6 : }
71 :
72 : /** Mock function for node_describe_longname_by_id, it returns either
73 : * STR_HSDIR_EXIST_LONGNAME or STR_HSDIR_NONE_EXIST_LONGNAME
74 : */
75 : static const char *
76 5 : node_describe_longname_by_id_replacement(const char *id_digest)
77 : {
78 5 : if (!strcmp(id_digest, HSDIR_EXIST_ID)) {
79 : return STR_HSDIR_EXIST_LONGNAME;
80 : } else {
81 0 : return STR_HSDIR_NONE_EXIST_LONGNAME;
82 : }
83 : }
84 :
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"
90 :
91 : static const node_t *
92 1 : mock_node_get_by_id(const char *digest)
93 : {
94 1 : static node_t node;
95 1 : memcpy(node.identity, digest, DIGEST_LEN);
96 1 : memset(node.hsdir_index.fetch, 'C', DIGEST256_LEN);
97 1 : memset(node.hsdir_index.store_first, 'D', DIGEST256_LEN);
98 1 : return &node;
99 : }
100 :
101 : static void
102 1 : test_hs_desc_event(void *arg)
103 : {
104 1 : int ret;
105 1 : char *expected_msg = NULL;
106 1 : char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
107 1 : ed25519_keypair_t identity_kp;
108 1 : ed25519_public_key_t blinded_pk;
109 1 : char base64_blinded_pk[ED25519_BASE64_LEN + 1];
110 1 : routerstatus_t hsdir_rs;
111 1 : hs_ident_dir_conn_t ident;
112 :
113 1 : (void) arg;
114 1 : MOCK(queue_control_event_string,
115 : queue_control_event_string_replacement);
116 1 : MOCK(node_describe_longname_by_id,
117 : node_describe_longname_by_id_replacement);
118 1 : MOCK(node_get_by_id, mock_node_get_by_id);
119 :
120 : /* Setup what we need for this test. */
121 1 : ed25519_keypair_generate(&identity_kp, 0);
122 1 : hs_build_address(&identity_kp.pubkey, HS_VERSION_THREE, onion_address);
123 1 : ret = hs_address_is_valid(onion_address);
124 1 : tt_int_op(ret, OP_EQ, 1);
125 1 : memset(&blinded_pk, 'B', sizeof(blinded_pk));
126 1 : memset(&hsdir_rs, 0, sizeof(hsdir_rs));
127 1 : memcpy(hsdir_rs.identity_digest, HSDIR_EXIST_ID, DIGEST_LEN);
128 1 : ed25519_public_to_base64(base64_blinded_pk, &blinded_pk);
129 1 : memcpy(&ident.identity_pk, &identity_kp.pubkey,
130 : sizeof(ed25519_public_key_t));
131 1 : memcpy(&ident.blinded_pk, &blinded_pk, sizeof(blinded_pk));
132 :
133 : /* HS_DESC REQUESTED ... */
134 1 : hs_control_desc_event_requested(&identity_kp.pubkey, base64_blinded_pk,
135 : &hsdir_rs);
136 1 : 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 1 : tt_assert(received_msg);
141 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
142 1 : tor_free(received_msg);
143 1 : tor_free(expected_msg);
144 :
145 : /* HS_DESC CREATED... */
146 1 : hs_control_desc_event_created(onion_address, &blinded_pk);
147 1 : tor_asprintf(&expected_msg, "650 HS_DESC CREATED %s UNKNOWN "
148 : "UNKNOWN %s\r\n",
149 : onion_address, base64_blinded_pk);
150 1 : tt_assert(received_msg);
151 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
152 1 : tor_free(received_msg);
153 1 : tor_free(expected_msg);
154 :
155 : /* HS_DESC UPLOAD... */
156 1 : uint8_t hsdir_index_store[DIGEST256_LEN];
157 1 : memset(hsdir_index_store, 'D', sizeof(hsdir_index_store));
158 1 : hs_control_desc_event_upload(onion_address, HSDIR_EXIST_ID,
159 : &blinded_pk, hsdir_index_store);
160 1 : 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 1 : tt_assert(received_msg);
165 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
166 1 : tor_free(received_msg);
167 1 : tor_free(expected_msg);
168 :
169 : /* HS_DESC FAILED... */
170 1 : hs_control_desc_event_failed(&ident, HSDIR_EXIST_ID, "BAD_DESC");
171 1 : 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 1 : tt_assert(received_msg);
176 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
177 1 : tor_free(received_msg);
178 1 : tor_free(expected_msg);
179 :
180 : /* HS_DESC RECEIVED... */
181 1 : hs_control_desc_event_received(&ident, HSDIR_EXIST_ID);
182 1 : 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 1 : tt_assert(received_msg);
186 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
187 1 : tor_free(received_msg);
188 1 : tor_free(expected_msg);
189 :
190 : /* HS_DESC UPLOADED... */
191 1 : hs_control_desc_event_uploaded(&ident, HSDIR_EXIST_ID);
192 1 : tor_asprintf(&expected_msg, "650 HS_DESC UPLOADED %s UNKNOWN "
193 : STR_HSDIR_EXIST_LONGNAME "\r\n",
194 : onion_address);
195 1 : tt_assert(received_msg);
196 1 : tt_str_op(received_msg, OP_EQ, expected_msg);
197 1 : tor_free(received_msg);
198 1 : tor_free(expected_msg);
199 :
200 1 : done:
201 1 : UNMOCK(queue_control_event_string);
202 1 : UNMOCK(node_describe_longname_by_id);
203 1 : UNMOCK(node_get_by_id);
204 1 : tor_free(received_msg);
205 1 : tor_free(expected_msg);
206 1 : }
207 :
208 : /** Test that we can correctly add, remove and view client auth credentials
209 : * using the control port. */
210 : static void
211 1 : test_hs_control_good_onion_client_auth_add(void *arg)
212 : {
213 1 : (void) arg;
214 :
215 1 : MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
216 :
217 1 : int retval;
218 1 : ed25519_public_key_t service_identity_pk_2fv, service_identity_pk_jt4,
219 : service_identity_pk_jam;
220 1 : control_connection_t conn;
221 1 : char *args = NULL;
222 1 : char *cp1 = NULL;
223 1 : size_t sz;
224 :
225 1 : hs_init();
226 :
227 : { /* Setup the control conn */
228 1 : memset(&conn, 0, sizeof(control_connection_t));
229 1 : TO_CONN(&conn)->outbuf = buf_new();
230 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
231 : }
232 :
233 : { /* Setup the services */
234 1 : retval = hs_parse_address(
235 : "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
236 : &service_identity_pk_2fv,
237 : NULL, NULL);
238 1 : tt_int_op(retval, OP_EQ, 0);
239 :
240 1 : retval = hs_parse_address(
241 : "jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd",
242 : &service_identity_pk_jt4,
243 : NULL, NULL);
244 1 : tt_int_op(retval, OP_EQ, 0);
245 :
246 1 : retval = hs_parse_address(
247 : "jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd",
248 : &service_identity_pk_jam,
249 : NULL, NULL);
250 1 : tt_int_op(retval, OP_EQ, 0);
251 : }
252 :
253 1 : digest256map_t *client_auths = get_hs_client_auths_map();
254 1 : tt_assert(!client_auths);
255 :
256 : /* Register first service */
257 1 : args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
258 : "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= ");
259 :
260 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
261 1 : tt_int_op(retval, OP_EQ, 0);
262 :
263 : /* Check contents */
264 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
265 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
266 :
267 1 : tor_free(cp1);
268 1 : tor_free(args);
269 :
270 : /* Register second service (even with an unrecognized argument) */
271 1 : args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
272 : "x25519:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA= DropSound=No");
273 :
274 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
275 1 : tt_int_op(retval, OP_EQ, 0);
276 :
277 : /* Check contents */
278 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
279 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
280 1 : tor_free(cp1);
281 1 : tor_free(args);
282 :
283 : /* Register second service (even with an unrecognized argument) */
284 1 : args = tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd "
285 : "x25519:FCV0c0ELDKKDpSFgVIB8Yow8Evj5iD+GoiTtK878NkQ= "
286 : "ClientName=MeganNicole ");
287 :
288 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
289 1 : tt_int_op(retval, OP_EQ, 0);
290 :
291 : /* Check contents */
292 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
293 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
294 1 : tor_free(cp1);
295 :
296 1 : client_auths = get_hs_client_auths_map();
297 1 : tt_assert(client_auths);
298 1 : tt_uint_op(digest256map_size(client_auths), OP_EQ, 3);
299 :
300 1 : hs_client_service_authorization_t *client_2fv =
301 1 : digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
302 1 : tt_assert(client_2fv);
303 1 : tt_int_op(client_2fv->flags, OP_EQ, 0);
304 :
305 1 : hs_client_service_authorization_t *client_jt4 =
306 1 : digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
307 1 : tt_assert(client_jt4);
308 1 : tt_int_op(client_jt4->flags, OP_EQ, 0);
309 :
310 1 : hs_client_service_authorization_t *client_jam =
311 1 : digest256map_get(client_auths, service_identity_pk_jam.pubkey);
312 1 : tt_assert(client_jam);
313 1 : tt_int_op(client_jam->flags, OP_EQ, 0);
314 :
315 : /* Now let's VIEW the auth credentials */
316 1 : tor_free(conn.current_cmd);
317 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
318 :
319 : /* First go with no arguments, so that we view all the credentials */
320 1 : tor_free(args);
321 1 : args = tor_strdup("");
322 :
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"
332 :
333 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
334 1 : tt_int_op(retval, OP_EQ, 0);
335 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
336 1 : tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NO_ADDR);
337 1 : tor_free(cp1);
338 :
339 : /* Now specify an HS addr, and see that we only view those creds */
340 1 : tor_free(args);
341 2 : args =
342 1 : tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
343 :
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"
349 :
350 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
351 1 : tt_int_op(retval, OP_EQ, 0);
352 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
353 1 : tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_JT4);
354 1 : tor_free(cp1);
355 :
356 : /* Now try to REMOVE the auth credentials */
357 1 : tor_free(conn.current_cmd);
358 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
359 :
360 : /* First try with a wrong addr */
361 1 : tor_free(args);
362 1 : args = tor_strdup("thatsok");
363 :
364 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
365 1 : tt_int_op(retval, OP_EQ, 0);
366 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
367 1 : tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"thatsok\"\r\n");
368 1 : tor_free(cp1);
369 :
370 1 : client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
371 1 : tt_assert(client_jt4);
372 :
373 : /* Now actually remove them. */
374 1 : tor_free(args);
375 1 : args =tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd");
376 :
377 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
378 1 : tt_int_op(retval, OP_EQ, 0);
379 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
380 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
381 1 : tor_free(cp1);
382 :
383 1 : client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
384 1 : tt_assert(!client_jt4);
385 :
386 : /* Now try another time (we should get 'already removed' msg) */
387 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
388 1 : tt_int_op(retval, OP_EQ, 0);
389 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
390 1 : tt_str_op(cp1, OP_EQ, "251 No credentials for "
391 : "\"jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd\"\r\n");
392 1 : tor_free(cp1);
393 :
394 1 : client_jt4 = digest256map_get(client_auths, service_identity_pk_jt4.pubkey);
395 1 : tt_assert(!client_jt4);
396 :
397 : /* Now also remove the other one */
398 1 : tor_free(args);
399 2 : args =
400 1 : tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
401 :
402 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
403 1 : tt_int_op(retval, OP_EQ, 0);
404 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
405 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
406 1 : tor_free(cp1);
407 :
408 : /* Now also remove the other one */
409 1 : tor_free(args);
410 2 : args =
411 1 : tor_strdup("jamie3vkiwibfiwucd6vxijskbhpjdyajmzeor4mc4i7yopvpo4p7cyd");
412 :
413 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
414 1 : tt_int_op(retval, OP_EQ, 0);
415 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
416 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
417 1 : tor_free(cp1);
418 :
419 : /* Finally, do another VIEW and see that we get nothing. */
420 1 : tor_free(conn.current_cmd);
421 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_VIEW");
422 1 : tor_free(args);
423 1 : args = tor_strdup("");
424 :
425 : #define VIEW_CORRECT_REPLY_NOTHING "250-ONION_CLIENT_AUTH_VIEW\r\n250 OK\r\n"
426 :
427 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
428 1 : tt_int_op(retval, OP_EQ, 0);
429 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
430 1 : tt_str_op(cp1, OP_EQ, VIEW_CORRECT_REPLY_NOTHING);
431 1 : tor_free(cp1);
432 :
433 : /* And a final VIEW with a wrong HS addr */
434 1 : tor_free(args);
435 1 : args = tor_strdup("house");
436 :
437 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
438 1 : tt_int_op(retval, OP_EQ, 0);
439 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
440 1 : tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"house\"\r\n");
441 :
442 1 : done:
443 1 : tor_free(args);
444 1 : tor_free(cp1);
445 1 : buf_free(TO_CONN(&conn)->outbuf);
446 1 : tor_free(conn.current_cmd);
447 1 : hs_client_free_all();
448 1 : }
449 :
450 : /** Test some error cases of ONION_CLIENT_AUTH_ADD */
451 : static void
452 1 : test_hs_control_bad_onion_client_auth_add(void *arg)
453 : {
454 1 : (void) arg;
455 :
456 1 : MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
457 :
458 1 : int retval;
459 1 : control_connection_t conn;
460 1 : char *cp1 = NULL;
461 1 : size_t sz;
462 1 : char *args = NULL;
463 :
464 1 : hs_init();
465 :
466 : { /* Setup the control conn */
467 1 : memset(&conn, 0, sizeof(control_connection_t));
468 1 : TO_CONN(&conn)->outbuf = buf_new();
469 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
470 : }
471 :
472 1 : digest256map_t *client_auths = get_hs_client_auths_map();
473 1 : tt_assert(!client_auths);
474 :
475 : /* Register first service */
476 1 : args = tor_strdup(
477 : "badaddr x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ=");
478 :
479 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
480 1 : tt_int_op(retval, OP_EQ, 0);
481 :
482 : /* Check contents */
483 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
484 1 : tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"badaddr\"\r\n");
485 :
486 1 : tor_free(cp1);
487 1 : tor_free(args);
488 :
489 : /* Register second service (even with an unrecognized argument) */
490 1 : args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
491 : "love:eIIdIGoSZwI2Q/lSzpf92akGki5I+PZIDz37MA5BhlA=");
492 :
493 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
494 1 : tt_int_op(retval, OP_EQ, 0);
495 :
496 : /* Check contents */
497 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
498 1 : tt_str_op(cp1, OP_EQ, "552 Unrecognized key type \"love\"\r\n");
499 :
500 1 : tor_free(cp1);
501 1 : tor_free(args);
502 :
503 : /* Register second service (even with an unrecognized argument) */
504 1 : args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
505 : "x25519:QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEK");
506 :
507 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
508 1 : tt_int_op(retval, OP_EQ, 0);
509 :
510 : /* Check contents */
511 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
512 1 : tt_str_op(cp1, OP_EQ, "512 Failed to decode x25519 private key\r\n");
513 :
514 1 : tor_free(cp1);
515 1 : tor_free(args);
516 :
517 : /* Register with an all zero client key */
518 1 : args = tor_strdup("jt4grrjwzyz3pjkylwfau5xnjaj23vxmhskqaeyfhrfylelw4hvxcuyd "
519 : "x25519:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
520 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
521 1 : tt_int_op(retval, OP_EQ, 0);
522 :
523 : /* Check contents */
524 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
525 1 : tt_str_op(cp1, OP_EQ, "553 Invalid private key \"AAAAAAAAAAAAAAAAAAAA"
526 : "AAAAAAAAAAAAAAAAAAAAAAA=\"\r\n");
527 :
528 1 : client_auths = get_hs_client_auths_map();
529 1 : tt_assert(!client_auths);
530 :
531 1 : done:
532 1 : tor_free(args);
533 1 : tor_free(cp1);
534 1 : buf_free(TO_CONN(&conn)->outbuf);
535 1 : tor_free(conn.current_cmd);
536 1 : hs_client_free_all();
537 1 : }
538 :
539 : /** Test that we can correctly add permanent client auth credentials using the
540 : * control port. */
541 : static void
542 1 : test_hs_control_store_permanent_creds(void *arg)
543 : {
544 1 : (void) arg;
545 :
546 1 : MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
547 :
548 1 : int retval;
549 1 : ed25519_public_key_t service_identity_pk_2fv;
550 1 : control_connection_t conn;
551 1 : char *args = NULL;
552 1 : char *cp1 = NULL;
553 1 : char *creds_file_str = NULL;
554 1 : char *creds_fname = NULL;
555 :
556 1 : size_t sz;
557 :
558 1 : hs_init();
559 :
560 : { /* Setup the control conn */
561 1 : memset(&conn, 0, sizeof(control_connection_t));
562 1 : TO_CONN(&conn)->outbuf = buf_new();
563 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_ADD");
564 : }
565 :
566 : { /* Setup the services */
567 1 : retval = hs_parse_address(
568 : "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd",
569 : &service_identity_pk_2fv,
570 : NULL, NULL);
571 1 : tt_int_op(retval, OP_EQ, 0);
572 : }
573 :
574 1 : digest256map_t *client_auths = get_hs_client_auths_map();
575 1 : tt_assert(!client_auths);
576 :
577 : /* Try registering first service with no ClientOnionAuthDir set */
578 1 : args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
579 : "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
580 : "Flags=Permanent");
581 :
582 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
583 1 : tt_int_op(retval, OP_EQ, 0);
584 :
585 : /* Check control port response. This one should fail. */
586 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
587 1 : tt_str_op(cp1, OP_EQ, "553 Unable to store creds for "
588 : "\"2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd\"\r\n");
589 :
590 : { /* Setup ClientOnionAuthDir */
591 1 : int ret;
592 1 : char *perm_creds_dir = tor_strdup(get_fname("permanent_credentials"));
593 1 : get_options_mutable()->ClientOnionAuthDir = perm_creds_dir;
594 :
595 : #ifdef _WIN32
596 : ret = mkdir(perm_creds_dir);
597 : #else
598 1 : ret = mkdir(perm_creds_dir, 0700);
599 : #endif
600 1 : tt_int_op(ret, OP_EQ, 0);
601 : }
602 :
603 1 : tor_free(args);
604 1 : tor_free(cp1);
605 :
606 : /* Try the control port command again. This time it should work! */
607 1 : args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
608 : "x25519:iJ1tjKCrMAbiFT2bVrCjhbfMDnE1fpaRbIS5ZHKUvEQ= "
609 : "Flags=Permanent");
610 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
611 1 : tt_int_op(retval, OP_EQ, 0);
612 :
613 : /* Check control port response */
614 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
615 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
616 :
617 : /* Check file contents! */
618 1 : creds_fname = tor_strdup(get_fname("permanent_credentials/"
619 : "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd.auth_private"));
620 1 : creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
621 :
622 1 : tt_assert(creds_file_str);
623 1 : tt_str_op(creds_file_str, OP_EQ,
624 : "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
625 : /* base32 representation of the base64 iJ1t... key above */
626 : "x25519:rcow3dfavmyanyqvhwnvnmfdqw34ydtrgv7jnelmqs4wi4uuxrca");
627 :
628 1 : tor_free(args);
629 1 : tor_free(cp1);
630 :
631 : /* Overwrite the credentials and check that they got overwrited. */
632 1 : args = tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd "
633 : "x25519:UDRvZLvcJo0QRLvDfkpgbtsqbkhIUQZyeo2FNBrgS18= "
634 : "Flags=Permanent");
635 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
636 1 : tt_int_op(retval, OP_EQ, 0);
637 :
638 : /* Check control port response: we replaced! */
639 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
640 1 : tt_str_op(cp1, OP_EQ, "251 Client for onion existed and replaced\r\n");
641 :
642 1 : tor_free(creds_file_str);
643 :
644 : /* Check creds file contents again. See that the key got updated */
645 1 : creds_file_str = read_file_to_str(creds_fname, RFTS_BIN, NULL);
646 1 : tt_assert(creds_file_str);
647 1 : tt_str_op(creds_file_str, OP_EQ,
648 : "2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd:descriptor:"
649 : /* base32 representation of the base64 UDRv... key above */
650 : "x25519:ka2g6zf33qti2ecexpbx4stan3nsu3sijbiqm4t2rwctigxajnpq");
651 :
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 1 : hs_config_client_authorization(get_options(), 0);
655 :
656 1 : client_auths = get_hs_client_auths_map();
657 1 : tt_assert(client_auths);
658 1 : tt_uint_op(digest256map_size(client_auths), OP_EQ, 1);
659 :
660 1 : hs_client_service_authorization_t *client_2fv =
661 1 : digest256map_get(client_auths, service_identity_pk_2fv.pubkey);
662 1 : tt_assert(client_2fv);
663 1 : tt_int_op(client_2fv->flags, OP_EQ, CLIENT_AUTH_FLAG_IS_PERMANENT);
664 1 : tt_str_op(hex_str((char*)client_2fv->enc_seckey.secret_key, 32), OP_EQ,
665 : "50346F64BBDC268D1044BBC37E4A606EDB2A6E48485106727A8D85341AE04B5F");
666 :
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 1 : tor_free(conn.current_cmd);
670 1 : tor_free(cp1);
671 1 : tor_free(args);
672 :
673 : /* Ensure that the creds file exists */
674 1 : tt_int_op(file_status(creds_fname), OP_EQ, FN_FILE);
675 :
676 : /* Do the REMOVE */
677 1 : conn.current_cmd = tor_strdup("ONION_CLIENT_AUTH_REMOVE");
678 1 : args =tor_strdup("2fvhjskjet3n5syd6yfg5lhvwcs62bojmthr35ko5bllr3iqdb4ctdyd");
679 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
680 1 : tt_int_op(retval, OP_EQ, 0);
681 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
682 1 : tt_str_op(cp1, OP_EQ, "250 OK\r\n");
683 :
684 : /* Ensure that the file has been removed and the map is empty */
685 1 : tt_int_op(file_status(creds_fname), OP_EQ, FN_NOENT);
686 1 : tt_uint_op(digest256map_size(client_auths), OP_EQ, 0);
687 :
688 1 : done:
689 1 : tor_free(get_options_mutable()->ClientOnionAuthDir);
690 1 : tor_free(args);
691 1 : tor_free(cp1);
692 1 : buf_free(TO_CONN(&conn)->outbuf);
693 1 : tor_free(conn.current_cmd);
694 1 : tor_free(creds_fname);
695 1 : tor_free(creds_file_str);
696 1 : hs_client_free_all();
697 1 : }
698 :
699 : /** Test that ADD_ONION properly handles an attacker passing it a bad private
700 : * key. */
701 : static void
702 1 : test_hs_control_add_onion_with_bad_pubkey(void *arg)
703 : {
704 1 : (void) arg;
705 :
706 1 : MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
707 :
708 1 : int retval;
709 1 : control_connection_t conn;
710 1 : char *args = NULL;
711 1 : char *cp1 = NULL;
712 1 : size_t sz;
713 :
714 1 : hs_init();
715 :
716 : { /* Setup the control conn */
717 1 : memset(&conn, 0, sizeof(control_connection_t));
718 1 : TO_CONN(&conn)->outbuf = buf_new();
719 1 : conn.current_cmd = tor_strdup("ADD_ONION");
720 : }
721 :
722 1 : args = tor_strdup("ED25519-V3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
723 : "AAAAAAAAAAAAAAAAAAAAAAA"
724 : "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA "
725 : "Port=9735,127.0.0.1 Flags=DiscardPK");
726 :
727 1 : retval = handle_control_command(&conn, (uint32_t) strlen(args), args);
728 1 : tt_int_op(retval, OP_EQ, 0);
729 :
730 : /* Check control port response */
731 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
732 1 : tt_str_op(cp1, OP_EQ, "551 Failed to generate onion address\r\n");
733 :
734 1 : done:
735 1 : tor_free(args);
736 1 : tor_free(cp1);
737 1 : buf_free(TO_CONN(&conn)->outbuf);
738 1 : tor_free(conn.current_cmd);
739 1 : }
740 :
741 : /** Test that we can add the service via the control port. */
742 : static void
743 1 : test_hs_control_add_auth_onion_service(void *arg)
744 : {
745 1 : control_connection_t conn;
746 1 : char *args = NULL, *cp1 = NULL;
747 1 : size_t sz;
748 :
749 1 : (void) arg;
750 :
751 1 : hs_init();
752 :
753 1 : memset(&conn, 0, sizeof(control_connection_t));
754 1 : TO_CONN(&conn)->outbuf = buf_new();
755 1 : conn.current_cmd = tor_strdup("ADD_ONION");
756 1 : args = tor_strdup("ED25519-V3:KLMQ4CLKwlDCHuMPn8j3od33cU5LhnrLNoZh7CWChl3VkY"
757 : "pNAkeP5dGW8xeKR9HxQBWQ/w7Kr12lA/U8Pd/oxw== "
758 : "ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja "
759 : "Flags=V3Auth Port=9735,127.0.0.1");
760 1 : handle_control_command(&conn, (uint32_t) strlen(args), args);
761 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
762 1 : tt_str_op(cp1, OP_EQ,
763 : "250-ServiceID=n35etu3yjxrqjpntmfziom5sjwspoydchmelc4xleoy4jk2u4lziz2yd\r\n"
764 : "250-ClientAuthV3=dz4q5xqlb4ldnbs72iarrml4ephk3du4i7o2cgiva5lwr6wkquja\r\n"
765 : "250 OK\r\n");
766 1 : tor_free(args);
767 1 : tor_free(cp1);
768 :
769 1 : 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 1 : handle_control_command(&conn, (uint32_t) strlen(args), args);
773 1 : cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz);
774 1 : tt_str_op(cp1, OP_EQ, "512 Cannot decode v3 client auth key\r\n");
775 :
776 1 : done:
777 1 : tor_free(args);
778 1 : tor_free(cp1);
779 1 : tor_free(conn.current_cmd);
780 1 : buf_free(TO_CONN(&conn)->outbuf);
781 2 : SMARTLIST_FOREACH(conn.ephemeral_onion_services, char *,
782 : service, tor_free(service));
783 1 : smartlist_free(conn.ephemeral_onion_services);
784 1 : hs_client_free_all();
785 1 : }
786 :
787 : /** Test that add_onion_helper_add_service can add the service. */
788 : static void
789 1 : test_hs_control_add_onion_helper_add_service(void *arg)
790 : {
791 1 : int hs_version_good, hs_version_bad;
792 1 : add_onion_secret_key_t sk_good, sk_bad;
793 1 : ed25519_public_key_t pk_good, pk_bad;
794 1 : char *key_new_blob_good = NULL, *key_new_blob_bad = NULL;
795 1 : const char *key_new_alg_good = NULL, *key_new_alg_bad = NULL;
796 1 : hs_service_authorized_client_t *client_good, *client_bad;
797 1 : smartlist_t *list_good, *list_bad;
798 1 : hs_service_ht *global_map;
799 1 : hs_port_config_t *portcfg;
800 1 : smartlist_t *portcfgs;
801 1 : char *address_out_good, *address_out_bad;
802 1 : hs_service_t *service_good = NULL;
803 1 : hs_service_t *service_bad = NULL;
804 :
805 1 : (void) arg;
806 :
807 1 : hs_init();
808 1 : global_map = get_hs_service_map();
809 :
810 1 : portcfg = hs_parse_port_config("8080", ",", NULL);
811 1 : portcfgs = smartlist_new();
812 1 : smartlist_add(portcfgs, portcfg);
813 :
814 1 : memset(&sk_good, 0, sizeof(sk_good));
815 1 : memset(&sk_bad, 0, sizeof(sk_bad));
816 :
817 1 : add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_good,
818 : &key_new_blob_good, &sk_good, &hs_version_good, NULL);
819 1 : add_onion_helper_keyarg("NEW:ED25519-V3", 0, &key_new_alg_bad,
820 : &key_new_blob_bad, &sk_bad, &hs_version_bad, NULL);
821 :
822 1 : ed25519_public_key_generate(&pk_good, sk_good.v3);
823 1 : ed25519_public_key_generate(&pk_bad, sk_bad.v3);
824 :
825 1 : client_good = parse_authorized_client_key(
826 : "N2NU7BSRL6YODZCYPN4CREB54TYLKGIE2KYOQWLFYC23ZJVCE5DQ", LOG_INFO);
827 1 : client_bad = parse_authorized_client_key("dummy", LOG_INFO);
828 :
829 1 : list_good = smartlist_new();
830 1 : smartlist_add(list_good, client_good);
831 :
832 1 : add_onion_helper_add_service(HS_VERSION_THREE, &sk_good, portcfgs, 1, 1,
833 : list_good, &address_out_good);
834 :
835 1 : service_good = find_service(global_map, &pk_good);
836 1 : tt_int_op(smartlist_len(service_good->config.clients), OP_EQ, 1);
837 :
838 1 : remove_service(global_map, service_good);
839 1 : hs_service_free(service_good);
840 :
841 1 : list_bad = smartlist_new();
842 1 : smartlist_add(list_bad, client_bad);
843 :
844 1 : portcfg = hs_parse_port_config("8080", ",", NULL);
845 1 : portcfgs = smartlist_new();
846 1 : smartlist_add(portcfgs, portcfg);
847 :
848 1 : add_onion_helper_add_service(HS_VERSION_THREE, &sk_bad, portcfgs, 1, 1,
849 : list_bad, &address_out_bad);
850 :
851 1 : service_bad = find_service(global_map, &pk_bad);
852 :
853 1 : tt_int_op(smartlist_len(service_bad->config.clients), OP_EQ, 0);
854 :
855 1 : done:
856 1 : tor_free(key_new_blob_good);
857 1 : tor_free(key_new_blob_bad);
858 1 : tor_free(address_out_good);
859 1 : tor_free(address_out_bad);
860 :
861 1 : hs_service_free(service_good);
862 1 : hs_service_free(service_bad);
863 1 : }
864 :
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},
882 :
883 : END_OF_TESTCASES
884 : };
|