Line data Source code
1 : /* Copyright (c) 2020-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file test_hs_ob.c
6 : * \brief Test hidden service onion balance functionality.
7 : */
8 :
9 : #define CONFIG_PRIVATE
10 : #define HS_SERVICE_PRIVATE
11 : #define HS_OB_PRIVATE
12 :
13 : #include "test/test.h"
14 : #include "test/test_helpers.h"
15 : #include "test/log_test_helpers.h"
16 :
17 : #include "app/config/config.h"
18 : #include "feature/hs/hs_config.h"
19 : #include "feature/hs/hs_ob.h"
20 : #include "feature/hs/hs_service.h"
21 : #include "feature/nodelist/networkstatus.h"
22 : #include "feature/nodelist/networkstatus_st.h"
23 :
24 : static ed25519_keypair_t onion_addr_kp_1;
25 : static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1];
26 :
27 : static ed25519_keypair_t onion_addr_kp_2;
28 : static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1];
29 :
30 : static bool config_is_good = true;
31 :
32 : static int
33 2 : helper_tor_config(const char *conf)
34 : {
35 2 : int ret = -1;
36 2 : or_options_t *options = helper_parse_options(conf);
37 2 : tt_assert(options);
38 2 : ret = hs_config_service_all(options, 0);
39 2 : done:
40 2 : or_options_free(options);
41 2 : return ret;
42 : }
43 :
44 : static networkstatus_t mock_ns;
45 :
46 : static networkstatus_t *
47 0 : mock_networkstatus_get_live_consensus(time_t now)
48 : {
49 0 : (void) now;
50 0 : return &mock_ns;
51 : }
52 :
53 : static char *
54 11 : mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out)
55 : {
56 11 : char *ret = NULL;
57 :
58 11 : (void) flags;
59 11 : (void) stat_out;
60 :
61 11 : if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR "ob_config"))) {
62 2 : if (config_is_good) {
63 1 : tor_asprintf(&ret, "MasterOnionAddress %s.onion\n"
64 : "MasterOnionAddress %s.onion\n",
65 : onion_addr_1, onion_addr_2);
66 : } else {
67 1 : tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n"
68 : "UnknownOption BLAH\n");
69 : }
70 2 : goto done;
71 : }
72 :
73 9 : done:
74 11 : return ret;
75 : }
76 :
77 : static void
78 1 : test_parse_config_file(void *arg)
79 : {
80 1 : int ret;
81 1 : char *conf = NULL;
82 1 : const ed25519_public_key_t *pkey;
83 :
84 1 : (void) arg;
85 :
86 1 : hs_init();
87 :
88 1 : MOCK(read_file_to_str, mock_read_file_to_str);
89 :
90 : #define fmt_conf \
91 : "HiddenServiceDir %s\n" \
92 : "HiddenServicePort 22\n" \
93 : "HiddenServiceOnionBalanceInstance 1\n"
94 1 : tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
95 : #undef fmt_conf
96 :
97 : /* Build the OB frontend onion addresses. */
98 1 : ed25519_keypair_generate(&onion_addr_kp_1, 0);
99 1 : hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1);
100 1 : ed25519_keypair_generate(&onion_addr_kp_2, 0);
101 1 : hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2);
102 :
103 1 : ret = helper_tor_config(conf);
104 1 : tor_free(conf);
105 1 : tt_int_op(ret, OP_EQ, 0);
106 :
107 : /* Load the keys for the service. After that, the v3 service should be
108 : * registered in the global map and we'll be able to access it. */
109 1 : tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1);
110 1 : hs_service_load_all_keys();
111 1 : tt_int_op(get_hs_service_map_size(), OP_EQ, 1);
112 1 : const hs_service_t *s = get_first_service();
113 1 : tt_assert(s);
114 1 : tt_assert(s->config.ob_master_pubkeys);
115 1 : tt_assert(hs_ob_service_is_instance(s));
116 1 : tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2);
117 :
118 : /* Test the public keys we've added. */
119 1 : pkey = smartlist_get(s->config.ob_master_pubkeys, 0);
120 1 : tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
121 1 : pkey = smartlist_get(s->config.ob_master_pubkeys, 1);
122 1 : tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN);
123 :
124 1 : done:
125 1 : hs_free_all();
126 :
127 1 : UNMOCK(read_file_to_str);
128 1 : }
129 :
130 : static void
131 1 : test_parse_config_file_bad(void *arg)
132 : {
133 1 : int ret;
134 1 : char *conf = NULL;
135 :
136 1 : (void) arg;
137 :
138 1 : hs_init();
139 :
140 1 : MOCK(read_file_to_str, mock_read_file_to_str);
141 :
142 : /* Indicate mock_read_file_to_str() to use the bad config. */
143 1 : config_is_good = false;
144 :
145 : #define fmt_conf \
146 : "HiddenServiceDir %s\n" \
147 : "HiddenServicePort 22\n" \
148 : "HiddenServiceOnionBalanceInstance 1\n"
149 1 : tor_asprintf(&conf, fmt_conf, get_fname("hs3"));
150 : #undef fmt_conf
151 :
152 1 : setup_full_capture_of_logs(LOG_INFO);
153 1 : ret = helper_tor_config(conf);
154 1 : tor_free(conf);
155 1 : tt_int_op(ret, OP_EQ, -1);
156 1 : expect_log_msg_containing("OnionBalance: MasterOnionAddress "
157 1 : "JUNKJUNKJUNK.onion is invalid");
158 1 : expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; "
159 1 : "saving it.");
160 1 : teardown_capture_of_logs();
161 :
162 1 : done:
163 1 : hs_free_all();
164 :
165 1 : UNMOCK(read_file_to_str);
166 1 : }
167 :
168 : static void
169 1 : test_get_subcredentials(void *arg)
170 : {
171 1 : int ret;
172 1 : hs_service_t *service = NULL;
173 1 : hs_service_config_t config;
174 1 : hs_subcredential_t *subcreds = NULL;
175 :
176 1 : (void) arg;
177 :
178 1 : MOCK(networkstatus_get_live_consensus,
179 : mock_networkstatus_get_live_consensus);
180 :
181 : /* Setup consensus with proper time so we can compute the time period. */
182 1 : ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC",
183 : &mock_ns.valid_after);
184 1 : tt_int_op(ret, OP_EQ, 0);
185 1 : ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC",
186 : &mock_ns.fresh_until);
187 1 : tt_int_op(ret, OP_EQ, 0);
188 :
189 1 : config.ob_master_pubkeys = smartlist_new();
190 1 : tt_assert(config.ob_master_pubkeys);
191 :
192 : /* Set up an instance */
193 1 : service = tor_malloc_zero(sizeof(hs_service_t));
194 1 : service->config = config;
195 : /* Setup the service descriptors */
196 1 : service->desc_current = service_descriptor_new();
197 1 : service->desc_next = service_descriptor_new();
198 :
199 : /* First try to compute subcredentials but with no OB keys. Make sure that
200 : * subcreds get NULLed. To do this check we first poison subcreds. */
201 1 : subcreds = (void*)999;
202 1 : tt_ptr_op(subcreds, OP_NE, NULL);
203 1 : size_t num = compute_subcredentials(service, &subcreds);
204 1 : tt_ptr_op(subcreds, OP_EQ, NULL);
205 :
206 : /* Generate a keypair to add to the OB keys list. */
207 1 : ed25519_keypair_generate(&onion_addr_kp_1, 0);
208 1 : smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey);
209 :
210 : /* Set up the instance subcredentials */
211 1 : char current_subcred[SUBCRED_LEN];
212 1 : char next_subcred[SUBCRED_LEN];
213 1 : memset(current_subcred, 'C', SUBCRED_LEN);
214 1 : memset(next_subcred, 'N', SUBCRED_LEN);
215 1 : memcpy(service->desc_current->desc->subcredential.subcred, current_subcred,
216 : SUBCRED_LEN);
217 1 : memcpy(service->desc_next->desc->subcredential.subcred, next_subcred,
218 : SUBCRED_LEN);
219 :
220 : /* See that subcreds are computed properly */
221 1 : num = compute_subcredentials(service, &subcreds);
222 : /* 5 subcredentials: 3 for the frontend, 2 for the instance */
223 1 : tt_uint_op(num, OP_EQ, 5);
224 1 : tt_ptr_op(subcreds, OP_NE, NULL);
225 :
226 : /* Validate the subcredentials we just got. We'll build them oursevles with
227 : * the right time period steps and compare. */
228 1 : const uint64_t tp = hs_get_time_period_num(0);
229 1 : const int steps[3] = {0, -1, 1};
230 :
231 1 : unsigned int i;
232 4 : for (i = 0; i < 3; i++) {
233 3 : hs_subcredential_t subcredential;
234 3 : ed25519_public_key_t blinded_pubkey;
235 3 : hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i],
236 : &blinded_pubkey);
237 3 : hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey,
238 : &subcredential);
239 3 : tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred,
240 3 : SUBCRED_LEN);
241 : }
242 :
243 1 : tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN);
244 1 : tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN);
245 :
246 1 : done:
247 1 : tor_free(subcreds);
248 :
249 1 : smartlist_free(config.ob_master_pubkeys);
250 1 : if (service) {
251 1 : memset(&service->config, 0, sizeof(hs_service_config_t));
252 1 : hs_service_free(service);
253 : }
254 :
255 1 : UNMOCK(networkstatus_get_live_consensus);
256 1 : }
257 :
258 : struct testcase_t hs_ob_tests[] = {
259 : { "parse_config_file", test_parse_config_file, TT_FORK,
260 : NULL, NULL },
261 : { "parse_config_file_bad", test_parse_config_file_bad, TT_FORK,
262 : NULL, NULL },
263 :
264 : { "get_subcredentials", test_get_subcredentials, TT_FORK,
265 : NULL, NULL },
266 :
267 : END_OF_TESTCASES
268 : };
|