1 /* Copyright (c) 2016-2021, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
3 
4 /**
5  * \file test_hs_config.c
6  * \brief Test hidden service configuration functionality.
7  */
8 
9 #define CONFIG_PRIVATE
10 #define HS_SERVICE_PRIVATE
11 
12 #include "test/test.h"
13 #include "test/test_helpers.h"
14 #include "test/log_test_helpers.h"
15 #include "test/resolve_test_helpers.h"
16 
17 #include "app/config/config.h"
18 #include "feature/hs/hs_common.h"
19 #include "feature/hs/hs_config.h"
20 #include "feature/hs/hs_service.h"
21 
22 static int
helper_config_service(const char * conf,int validate_only)23 helper_config_service(const char *conf, int validate_only)
24 {
25   int ret = 0;
26   or_options_t *options = NULL;
27   tt_assert(conf);
28   options = helper_parse_options(conf);
29   tt_assert(options);
30   ret = hs_config_service_all(options, validate_only);
31  done:
32   or_options_free(options);
33   return ret;
34 }
35 
36 static void
test_invalid_service(void * arg)37 test_invalid_service(void *arg)
38 {
39   int ret;
40 
41   (void) arg;
42 
43   /* Try with a missing port configuration. */
44   {
45     const char *conf =
46       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
47       "HiddenServiceVersion 1\n"; /* Wrong not supported version. */
48     setup_full_capture_of_logs(LOG_WARN);
49     ret = helper_config_service(conf, 1);
50     tt_int_op(ret, OP_EQ, -1);
51     expect_log_msg_containing("HiddenServiceVersion must be 3, not 1");
52     teardown_capture_of_logs();
53   }
54 
55   /* Bad value of HiddenServiceAllowUnknownPorts. */
56   {
57     const char *conf =
58       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
59       "HiddenServiceVersion 3\n"
60       "HiddenServiceAllowUnknownPorts 2\n"; /* Should be 0 or 1. */
61     setup_full_capture_of_logs(LOG_WARN);
62     ret = helper_config_service(conf, 1);
63     tt_int_op(ret, OP_EQ, -1);
64     expect_log_msg_containing("Could not parse "
65                               "HiddenServiceAllowUnknownPorts: Unrecognized "
66                               "value 2. Allowed values are 0 and 1.");
67     teardown_capture_of_logs();
68   }
69 
70   /* Bad value of HiddenServiceDirGroupReadable */
71   {
72     const char *conf =
73       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
74       "HiddenServiceVersion 3\n"
75       "HiddenServiceDirGroupReadable 2\n"; /* Should be 0 or 1. */
76     setup_full_capture_of_logs(LOG_WARN);
77     ret = helper_config_service(conf, 1);
78     tt_int_op(ret, OP_EQ, -1);
79     expect_log_msg_containing("Could not parse "
80                               "HiddenServiceDirGroupReadable: "
81                               "Unrecognized value 2.");
82     teardown_capture_of_logs();
83   }
84 
85   /* Bad value of HiddenServiceMaxStreamsCloseCircuit */
86   {
87     const char *conf =
88       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
89       "HiddenServiceVersion 3\n"
90       "HiddenServiceMaxStreamsCloseCircuit 2\n"; /* Should be 0 or 1. */
91     setup_full_capture_of_logs(LOG_WARN);
92     ret = helper_config_service(conf, 1);
93     tt_int_op(ret, OP_EQ, -1);
94     expect_log_msg_containing("Could not parse "
95                               "HiddenServiceMaxStreamsCloseCircuit: "
96                               "Unrecognized value 2");
97     teardown_capture_of_logs();
98   }
99 
100   /* Too much max streams. */
101   {
102     const char *conf =
103       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
104       "HiddenServiceVersion 3\n"
105       "HiddenServicePort 80\n"
106       "HiddenServiceMaxStreams 65536\n"; /* One too many. */
107     setup_full_capture_of_logs(LOG_WARN);
108     ret = helper_config_service(conf, 1);
109     tt_int_op(ret, OP_EQ, -1);
110     expect_log_msg_containing("HiddenServiceMaxStreams must be between "
111                               "0 and 65535, not 65536");
112     teardown_capture_of_logs();
113   }
114 
115   /* Duplicate directory directive. */
116   {
117     const char *conf =
118       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
119       "HiddenServiceVersion 3\n"
120       "HiddenServicePort 80\n"
121       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
122       "HiddenServiceVersion 3\n"
123       "HiddenServicePort 81\n";
124     setup_full_capture_of_logs(LOG_WARN);
125     ret = helper_config_service(conf, 1);
126     tt_int_op(ret, OP_EQ, -1);
127     expect_log_msg_containing("Another hidden service is already "
128                               "configured for directory");
129     teardown_capture_of_logs();
130   }
131 
132   /* Bad port. */
133   {
134     const char *conf =
135       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
136       "HiddenServiceVersion 3\n"
137       "HiddenServicePort 65536\n";
138     setup_full_capture_of_logs(LOG_WARN);
139     ret = helper_config_service(conf, 1);
140     tt_int_op(ret, OP_EQ, -1);
141     expect_log_msg_containing("Missing or invalid port");
142     teardown_capture_of_logs();
143   }
144 
145   /* Bad target addr:port separation. */
146   {
147     const char *conf =
148       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
149       "HiddenServiceVersion 3\n"
150       "HiddenServicePort 80 127.0.0.1 8000\n";
151     setup_full_capture_of_logs(LOG_WARN);
152     ret = helper_config_service(conf, 1);
153     tt_int_op(ret, OP_EQ, -1);
154     expect_log_msg_containing("HiddenServicePort parse error: "
155                               "invalid port mapping");
156     teardown_capture_of_logs();
157   }
158 
159   /* Out of order directives. */
160   {
161     const char *conf =
162       "HiddenServiceVersion 3\n"
163       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
164       "HiddenServicePort 80\n";
165     setup_full_capture_of_logs(LOG_WARN);
166     ret = helper_config_service(conf, 1);
167     tt_int_op(ret, OP_EQ, -1);
168     expect_log_msg_containing("HiddenServiceVersion with no preceding "
169                               "HiddenServiceDir directive");
170     teardown_capture_of_logs();
171   }
172 
173  done:
174   ;
175 }
176 
177 static void
test_valid_service(void * arg)178 test_valid_service(void *arg)
179 {
180   int ret;
181 
182   (void) arg;
183 
184   {
185     const char *conf =
186       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
187       "HiddenServiceVersion 3\n"
188       "HiddenServicePort 81\n";
189     ret = helper_config_service(conf, 1);
190     tt_int_op(ret, OP_EQ, 0);
191   }
192 
193  done:
194   ;
195 }
196 
197 static void
test_invalid_service_v3(void * arg)198 test_invalid_service_v3(void *arg)
199 {
200   int validate_only = 1, ret;
201 
202   (void) arg;
203 
204   /* Try with a missing port configuration. */
205   {
206     const char *conf =
207       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
208       "HiddenServiceVersion 3\n";
209     setup_full_capture_of_logs(LOG_WARN);
210     ret = helper_config_service(conf, validate_only);
211     tt_int_op(ret, OP_EQ, -1);
212     expect_log_msg_containing("with no ports configured.");
213     teardown_capture_of_logs();
214   }
215 
216   /* Too many introduction points. */
217   {
218     const char *conf =
219       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
220       "HiddenServiceVersion 3\n"
221       "HiddenServicePort 80\n"
222       "HiddenServiceNumIntroductionPoints 21\n"; /* One too many. */
223     setup_full_capture_of_logs(LOG_WARN);
224     ret = helper_config_service(conf, validate_only);
225     tt_int_op(ret, OP_EQ, -1);
226     expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
227                               "be between 3 and 20, not 21.");
228     teardown_capture_of_logs();
229   }
230 
231   /* Too little introduction points. */
232   {
233     const char *conf =
234       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
235       "HiddenServiceVersion 3\n"
236       "HiddenServicePort 80\n"
237       "HiddenServiceNumIntroductionPoints 1\n";
238     setup_full_capture_of_logs(LOG_WARN);
239     ret = helper_config_service(conf, validate_only);
240     tt_int_op(ret, OP_EQ, -1);
241     expect_log_msg_containing("HiddenServiceNumIntroductionPoints must "
242                               "be between 3 and 20, not 1.");
243     teardown_capture_of_logs();
244   }
245 
246  done:
247   ;
248 }
249 
250 static void
test_valid_service_v3(void * arg)251 test_valid_service_v3(void *arg)
252 {
253   int ret;
254 
255   (void) arg;
256   mock_hostname_resolver();
257 
258   /* Valid complex configuration. */
259   {
260     const char *conf =
261       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs1\n"
262       "HiddenServiceVersion 3\n"
263       "HiddenServicePort 80\n"
264       "HiddenServicePort 22 localhost:22\n"
265 #ifdef HAVE_SYS_UN_H
266       "HiddenServicePort 42 unix:/path/to/socket\n"
267 #endif
268       "HiddenServiceAllowUnknownPorts 1\n"
269       "HiddenServiceMaxStreams 42\n"
270       "HiddenServiceMaxStreamsCloseCircuit 0\n"
271       "HiddenServiceDirGroupReadable 1\n"
272       "HiddenServiceNumIntroductionPoints 7\n";
273     ret = helper_config_service(conf, 1);
274     tt_int_op(ret, OP_EQ, 0);
275   }
276 
277   /* Valid complex configuration. */
278   {
279     const char *conf =
280       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
281       "HiddenServiceVersion 3\n"
282       "HiddenServicePort 65535\n"
283       "HiddenServicePort 22 1.1.1.1:22\n"
284 #ifdef HAVE_SYS_UN_H
285       "HiddenServicePort 9000 unix:/path/to/socket\n"
286 #endif
287       "HiddenServiceAllowUnknownPorts 0\n"
288       "HiddenServiceMaxStreams 42\n"
289       "HiddenServiceMaxStreamsCloseCircuit 0\n"
290       "HiddenServiceDirGroupReadable 1\n"
291       "HiddenServiceNumIntroductionPoints 20\n";
292     ret = helper_config_service(conf, 1);
293     tt_int_op(ret, OP_EQ, 0);
294   }
295 
296  done:
297   unmock_hostname_resolver();
298 }
299 
300 static void
test_staging_service_v3(void * arg)301 test_staging_service_v3(void *arg)
302 {
303   int ret;
304 
305   (void) arg;
306 
307   /* We don't validate a service object, this is the service test that are in
308    * charge of doing so. We just check for the stable state after
309    * registration. */
310 
311   hs_init();
312 
313   /* Time for a valid v3 service that should get staged. */
314   const char *conf =
315     "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs2\n"
316     "HiddenServiceVersion 3\n"
317     "HiddenServicePort 65535\n"
318     "HiddenServicePort 22 1.1.1.1:22\n"
319 #ifdef HAVE_SYS_UN_H
320     "HiddenServicePort 9000 unix:/path/to/socket\n"
321 #endif
322     "HiddenServiceAllowUnknownPorts 0\n"
323     "HiddenServiceMaxStreams 42\n"
324     "HiddenServiceMaxStreamsCloseCircuit 0\n"
325     "HiddenServiceDirGroupReadable 1\n"
326     "HiddenServiceNumIntroductionPoints 20\n";
327   ret = helper_config_service(conf, 0);
328   tt_int_op(ret, OP_EQ, 0);
329   /* Ok, we have a service in our map! Registration went well. */
330   tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
331 
332  done:
333   hs_free_all();
334 }
335 
336 static void
test_dos_parameters(void * arg)337 test_dos_parameters(void *arg)
338 {
339   int ret;
340 
341   (void) arg;
342 
343   hs_init();
344 
345   /* Valid configuration. */
346   {
347     const char *conf =
348       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
349       "HiddenServiceVersion 3\n"
350       "HiddenServicePort 22 1.1.1.1:22\n"
351       "HiddenServiceEnableIntroDoSDefense 1\n"
352       "HiddenServiceEnableIntroDoSRatePerSec 42\n"
353       "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
354 
355     setup_full_capture_of_logs(LOG_INFO);
356     ret = helper_config_service(conf, 0);
357     tt_int_op(ret, OP_EQ, 0);
358     expect_log_msg_containing("Service INTRO2 DoS defenses rate set to: 42");
359     expect_log_msg_containing("Service INTRO2 DoS defenses burst set to: 87");
360     teardown_capture_of_logs();
361   }
362 
363   /* Invalid rate. Value of 2^37. Max allowed is 2^31. */
364   {
365     const char *conf =
366       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
367       "HiddenServiceVersion 3\n"
368       "HiddenServicePort 22 1.1.1.1:22\n"
369       "HiddenServiceEnableIntroDoSDefense 1\n"
370       "HiddenServiceEnableIntroDoSRatePerSec 137438953472\n"
371       "HiddenServiceEnableIntroDoSBurstPerSec 87\n";
372 
373     setup_full_capture_of_logs(LOG_WARN);
374     ret = helper_config_service(conf, 0);
375     tt_int_op(ret, OP_EQ, -1);
376     expect_log_msg_containing("Could not parse "
377                               "HiddenServiceEnableIntroDoSRatePerSec: "
378                               "Integer 137438953472 is malformed or out of "
379                               "bounds.");
380     teardown_capture_of_logs();
381   }
382 
383   /* Invalid burst. Value of 2^38. Max allowed is 2^31. */
384   {
385     const char *conf =
386       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
387       "HiddenServiceVersion 3\n"
388       "HiddenServicePort 22 1.1.1.1:22\n"
389       "HiddenServiceEnableIntroDoSDefense 1\n"
390       "HiddenServiceEnableIntroDoSRatePerSec 42\n"
391       "HiddenServiceEnableIntroDoSBurstPerSec 274877906944\n";
392 
393     setup_full_capture_of_logs(LOG_WARN);
394     ret = helper_config_service(conf, 0);
395     tt_int_op(ret, OP_EQ, -1);
396     expect_log_msg_containing("Could not parse "
397                               "HiddenServiceEnableIntroDoSBurstPerSec: "
398                               "Integer 274877906944 is malformed or out "
399                               "of bounds.");
400     teardown_capture_of_logs();
401   }
402 
403   /* Burst is smaller than rate. */
404   {
405     const char *conf =
406       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
407       "HiddenServiceVersion 3\n"
408       "HiddenServicePort 22 1.1.1.1:22\n"
409       "HiddenServiceEnableIntroDoSDefense 1\n"
410       "HiddenServiceEnableIntroDoSRatePerSec 42\n"
411       "HiddenServiceEnableIntroDoSBurstPerSec 27\n";
412 
413     setup_full_capture_of_logs(LOG_WARN);
414     ret = helper_config_service(conf, 0);
415     tt_int_op(ret, OP_EQ, -1);
416     expect_log_msg_containing("Hidden service DoS defenses burst (27) can "
417                               "not be smaller than the rate value (42).");
418     teardown_capture_of_logs();
419   }
420 
421   /* Negative value. */
422   {
423     const char *conf =
424       "HiddenServiceDir /tmp/tor-test-hs-RANDOM/hs3\n"
425       "HiddenServiceVersion 3\n"
426       "HiddenServicePort 22 1.1.1.1:22\n"
427       "HiddenServiceEnableIntroDoSDefense 1\n"
428       "HiddenServiceEnableIntroDoSRatePerSec -1\n"
429       "HiddenServiceEnableIntroDoSBurstPerSec 42\n";
430 
431     setup_full_capture_of_logs(LOG_WARN);
432     ret = helper_config_service(conf, 0);
433     tt_int_op(ret, OP_EQ, -1);
434     expect_log_msg_containing("Could not parse "
435                               "HiddenServiceEnableIntroDoSRatePerSec: "
436                               "Integer -1 is malformed or out of bounds.");
437     teardown_capture_of_logs();
438   }
439 
440  done:
441   hs_free_all();
442 }
443 
444 struct testcase_t hs_config_tests[] = {
445   /* Invalid service not specific to any version. */
446   { "invalid_service", test_invalid_service, TT_FORK,
447     NULL, NULL },
448   { "valid_service", test_valid_service, TT_FORK,
449     NULL, NULL },
450 
451   /* Test case only for version 3. */
452   { "invalid_service_v3", test_invalid_service_v3, TT_FORK,
453     NULL, NULL },
454   { "valid_service_v3", test_valid_service_v3, TT_FORK,
455     NULL, NULL },
456 
457   /* Test service staging. */
458   { "staging_service_v3", test_staging_service_v3, TT_FORK,
459     NULL, NULL },
460 
461   /* Test HS DoS parameters. */
462   { "dos_parameters", test_dos_parameters, TT_FORK,
463     NULL, NULL },
464 
465   END_OF_TESTCASES
466 };
467