Line data Source code
1 : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file hs_ob.c
6 : * \brief Implement Onion Balance specific code.
7 : **/
8 :
9 : #define HS_OB_PRIVATE
10 :
11 : #include "feature/hs/hs_service.h"
12 :
13 : #include "feature/nodelist/networkstatus.h"
14 : #include "feature/nodelist/networkstatus_st.h"
15 :
16 : #include "lib/confmgt/confmgt.h"
17 : #include "lib/encoding/confline.h"
18 :
19 : #include "feature/hs/hs_ob.h"
20 :
21 : /* Options config magic number. */
22 : #define OB_OPTIONS_MAGIC 0x631DE7EA
23 :
24 : /* Helper macros. */
25 : #define VAR(varname, conftype, member, initvalue) \
26 : CONFIG_VAR_ETYPE(ob_options_t, varname, conftype, member, 0, initvalue)
27 : #define V(member,conftype,initvalue) \
28 : VAR(#member, conftype, member, initvalue)
29 :
30 : /* Dummy instance of ob_options_t, used for type-checking its members with
31 : * CONF_CHECK_VAR_TYPE. */
32 : DUMMY_TYPECHECK_INSTANCE(ob_options_t);
33 :
34 : /* Array of variables for the config file options. */
35 : static const config_var_t config_vars[] = {
36 : V(MasterOnionAddress, LINELIST, NULL),
37 :
38 : END_OF_CONFIG_VARS
39 : };
40 :
41 : /* "Extra" variable in the state that receives lines we can't parse. This
42 : * lets us preserve options from versions of Tor newer than us. */
43 : static const struct_member_t config_extra_vars = {
44 : .name = "__extra",
45 : .type = CONFIG_TYPE_LINELIST,
46 : .offset = offsetof(ob_options_t, ExtraLines),
47 : };
48 :
49 : /* Configuration format of ob_options_t. */
50 : static const config_format_t config_format = {
51 : .size = sizeof(ob_options_t),
52 : .magic = {
53 : "ob_options_t",
54 : OB_OPTIONS_MAGIC,
55 : offsetof(ob_options_t, magic_),
56 : },
57 : .vars = config_vars,
58 : .extra = &config_extra_vars,
59 : };
60 :
61 : /* Global configuration manager for the config file. */
62 : static config_mgr_t *config_options_mgr = NULL;
63 :
64 : /* Return the configuration manager for the config file. */
65 : static const config_mgr_t *
66 8 : get_config_options_mgr(void)
67 : {
68 8 : if (PREDICT_UNLIKELY(config_options_mgr == NULL)) {
69 2 : config_options_mgr = config_mgr_new(&config_format);
70 2 : config_mgr_freeze(config_options_mgr);
71 : }
72 8 : return config_options_mgr;
73 : }
74 :
75 : #define ob_option_free(val) \
76 : FREE_AND_NULL(ob_options_t, ob_option_free_, (val))
77 :
78 : /** Helper: Free a config options object. */
79 : static void
80 2 : ob_option_free_(ob_options_t *opts)
81 : {
82 2 : if (opts == NULL) {
83 : return;
84 : }
85 2 : config_free(get_config_options_mgr(), opts);
86 : }
87 :
88 : /** Return an allocated config options object. */
89 : static ob_options_t *
90 2 : ob_option_new(void)
91 : {
92 2 : ob_options_t *opts = config_new(get_config_options_mgr());
93 2 : config_init(get_config_options_mgr(), opts);
94 2 : return opts;
95 : }
96 :
97 : /** Helper function: From the configuration line value which is an onion
98 : * address with the ".onion" extension, find the public key and put it in
99 : * pkey_out.
100 : *
101 : * On success, true is returned. Else, false and pkey is untouched. */
102 : static bool
103 3 : get_onion_public_key(const char *value, ed25519_public_key_t *pkey_out)
104 : {
105 3 : char address[HS_SERVICE_ADDR_LEN_BASE32 + 1];
106 :
107 3 : tor_assert(value);
108 3 : tor_assert(pkey_out);
109 :
110 3 : if (strcmpend(value, ".onion")) {
111 : /* Not a .onion extension, bad format. */
112 : return false;
113 : }
114 :
115 : /* Length validation. The -1 is because sizeof() counts the NUL byte. */
116 3 : if (strlen(value) >
117 : (HS_SERVICE_ADDR_LEN_BASE32 + sizeof(".onion") - 1)) {
118 : /* Too long, bad format. */
119 : return false;
120 : }
121 :
122 : /* We don't want the .onion so we add 2 because size - 1 is copied with
123 : * strlcpy() in order to accommodate the NUL byte and sizeof() counts the NUL
124 : * byte so we need to remove them from the equation. */
125 3 : strlcpy(address, value, strlen(value) - sizeof(".onion") + 2);
126 :
127 3 : if (hs_parse_address_no_log(address, pkey_out, NULL, NULL, NULL) < 0) {
128 1 : return false;
129 : }
130 :
131 : /* Success. */
132 : return true;
133 : }
134 :
135 : /** Parse the given ob options in opts and set the service config object
136 : * accordingly.
137 : *
138 : * Return 1 on success else 0. */
139 : static int
140 2 : ob_option_parse(hs_service_config_t *config, const ob_options_t *opts)
141 : {
142 2 : int ret = 0;
143 2 : config_line_t *line;
144 :
145 2 : tor_assert(config);
146 2 : tor_assert(opts);
147 :
148 4 : for (line = opts->MasterOnionAddress; line; line = line->next) {
149 : /* Allocate config list if need be. */
150 3 : if (!config->ob_master_pubkeys) {
151 2 : config->ob_master_pubkeys = smartlist_new();
152 : }
153 3 : ed25519_public_key_t *pubkey = tor_malloc_zero(sizeof(*pubkey));
154 :
155 3 : if (!get_onion_public_key(line->value, pubkey)) {
156 1 : log_warn(LD_REND, "OnionBalance: MasterOnionAddress %s is invalid",
157 : line->value);
158 1 : tor_free(pubkey);
159 1 : goto end;
160 : }
161 2 : smartlist_add(config->ob_master_pubkeys, pubkey);
162 2 : log_notice(LD_REND, "OnionBalance: MasterOnionAddress %s registered",
163 : line->value);
164 : }
165 : /* Success. */
166 : ret = 1;
167 :
168 2 : end:
169 : /* No keys added, we free the list since no list means no onion balance
170 : * support for this tor instance. */
171 2 : if (smartlist_len(config->ob_master_pubkeys) == 0) {
172 1 : smartlist_free(config->ob_master_pubkeys);
173 : }
174 2 : return ret;
175 : }
176 :
177 : /** For the given master public key and time period, compute the subcredential
178 : * and put them into subcredential. The subcredential parameter needs to be at
179 : * least DIGEST256_LEN in size. */
180 : static void
181 9 : build_subcredential(const ed25519_public_key_t *pkey, uint64_t tp,
182 : hs_subcredential_t *subcredential)
183 : {
184 9 : ed25519_public_key_t blinded_pubkey;
185 :
186 9 : tor_assert(pkey);
187 9 : tor_assert(subcredential);
188 :
189 9 : hs_build_blinded_pubkey(pkey, NULL, 0, tp, &blinded_pubkey);
190 9 : hs_get_subcredential(pkey, &blinded_pubkey, subcredential);
191 9 : }
192 :
193 : /*
194 : * Public API.
195 : */
196 :
197 : /** Return true iff the given service is configured as an onion balance
198 : * instance. To satisfy that condition, there must at least be one master
199 : * ed25519 public key configured. */
200 : bool
201 55 : hs_ob_service_is_instance(const hs_service_t *service)
202 : {
203 55 : if (BUG(service == NULL)) {
204 0 : return false;
205 : }
206 :
207 : /* No list, we are not an instance. */
208 55 : if (!service->config.ob_master_pubkeys) {
209 : return false;
210 : }
211 :
212 9 : return smartlist_len(service->config.ob_master_pubkeys) > 0;
213 : }
214 :
215 : /** Read and parse the config file at fname on disk. The service config object
216 : * is populated with the options if any.
217 : *
218 : * Return 1 on success else 0. This is to follow the "ok" convention in
219 : * hs_config.c. */
220 : int
221 2 : hs_ob_parse_config_file(hs_service_config_t *config)
222 : {
223 2 : static const char *fname = "ob_config";
224 2 : int ret = 0;
225 2 : char *content = NULL, *errmsg = NULL, *config_file_path = NULL;
226 2 : ob_options_t *options = NULL;
227 2 : config_line_t *lines = NULL;
228 :
229 2 : tor_assert(config);
230 :
231 : /* Read file from disk. */
232 2 : config_file_path = hs_path_from_filename(config->directory_path, fname);
233 2 : content = read_file_to_str(config_file_path, 0, NULL);
234 2 : if (!content) {
235 0 : log_warn(LD_FS, "OnionBalance: Unable to read config file %s",
236 : escaped(config_file_path));
237 0 : goto end;
238 : }
239 :
240 : /* Parse lines. */
241 2 : if (config_get_lines(content, &lines, 0) < 0) {
242 0 : goto end;
243 : }
244 :
245 2 : options = ob_option_new();
246 2 : config_assign(get_config_options_mgr(), options, lines, 0, &errmsg);
247 2 : if (errmsg) {
248 0 : log_warn(LD_REND, "OnionBalance: Unable to parse config file: %s",
249 : errmsg);
250 0 : tor_free(errmsg);
251 0 : goto end;
252 : }
253 :
254 : /* Parse the options and set the service config object with the details. */
255 2 : ret = ob_option_parse(config, options);
256 :
257 2 : end:
258 2 : config_free_lines(lines);
259 2 : ob_option_free(options);
260 2 : tor_free(content);
261 2 : tor_free(config_file_path);
262 2 : return ret;
263 : }
264 :
265 : /** Compute all possible subcredentials for every onion master key in the given
266 : * service config object. subcredentials_out is allocated and set as an
267 : * continuous array containing all possible values.
268 : *
269 : * On success, return the number of subcredential put in the array which will
270 : * correspond to an array of size: n * DIGEST256_LEN where DIGEST256_LEN is the
271 : * length of a single subcredential.
272 : *
273 : * If the given configuration object has no OB master keys configured, 0 is
274 : * returned and subcredentials_out is set to NULL.
275 : *
276 : * Otherwise, this can't fail. */
277 : STATIC size_t
278 4 : compute_subcredentials(const hs_service_t *service,
279 : hs_subcredential_t **subcredentials_out)
280 : {
281 4 : unsigned int num_pkeys, idx = 0;
282 4 : hs_subcredential_t *subcreds = NULL;
283 4 : const int steps[3] = {0, -1, 1};
284 4 : const unsigned int num_steps = ARRAY_LENGTH(steps);
285 4 : const uint64_t tp = hs_get_time_period_num(0);
286 :
287 4 : tor_assert(service);
288 4 : tor_assert(subcredentials_out);
289 : /* Our caller has checked these too */
290 4 : tor_assert(service->desc_current);
291 4 : tor_assert(service->desc_next);
292 :
293 : /* Make sure we are an OB instance, or bail out. */
294 4 : num_pkeys = smartlist_len(service->config.ob_master_pubkeys);
295 4 : if (!num_pkeys) {
296 1 : *subcredentials_out = NULL;
297 1 : return 0;
298 : }
299 :
300 : /* Time to build all the subcredentials for each time period: two for each
301 : * instance descriptor plus three for the onionbalance frontend service: the
302 : * previous one (-1), the current one (0) and the next one (1) for each
303 : * configured key in order to accommodate client and service consensus skew.
304 : *
305 : * If the client consensus after_time is at 23:00 but the service one is at
306 : * 01:00, the client will be using the previous time period where the
307 : * service will think it is the client next time period. Thus why we have
308 : * to try them all.
309 : *
310 : * The normal use case works because the service gets the descriptor object
311 : * that corresponds to the intro point's request, and because each
312 : * descriptor corresponds to a specific subcredential, we get the right
313 : * subcredential out of it, and use that to do the decryption.
314 : *
315 : * As a slight optimization, statistically, the current time period (0) will
316 : * be the one to work first so we'll put them first in the array to maximize
317 : * our chance of success. */
318 :
319 : /* We use a flat array, not a smartlist_t, in order to minimize memory
320 : * allocation.
321 : *
322 : * Size of array is: length of a single subcredential multiplied by the
323 : * number of time period we need to compute and finally multiplied by the
324 : * total number of keys we are about to process. In other words, for each
325 : * key, we allocate 3 subcredential slots. Then in the end we also add two
326 : * subcredentials for this instance's active descriptors. */
327 3 : subcreds =
328 3 : tor_calloc((num_steps * num_pkeys) + 2, sizeof(hs_subcredential_t));
329 :
330 : /* For each master pubkey we add 3 subcredentials: */
331 12 : for (unsigned int i = 0; i < num_steps; i++) {
332 18 : SMARTLIST_FOREACH_BEGIN(service->config.ob_master_pubkeys,
333 : const ed25519_public_key_t *, pkey) {
334 9 : build_subcredential(pkey, tp + steps[i], &subcreds[idx]);
335 9 : idx++;
336 9 : } SMARTLIST_FOREACH_END(pkey);
337 : }
338 :
339 : /* And then in the end we add the two subcredentials of the current active
340 : * instance descriptors */
341 3 : memcpy(&subcreds[idx++], &service->desc_current->desc->subcredential,
342 : sizeof(hs_subcredential_t));
343 3 : memcpy(&subcreds[idx++], &service->desc_next->desc->subcredential,
344 : sizeof(hs_subcredential_t));
345 :
346 3 : log_info(LD_REND, "Refreshing %u onionbalance keys (TP #%d).",
347 : idx, (int)tp);
348 :
349 3 : *subcredentials_out = subcreds;
350 3 : return idx;
351 : }
352 :
353 : /**
354 : * If we are an Onionbalance instance, refresh our keys.
355 : *
356 : * If we are not an Onionbalance instance or we are not ready to do so, this
357 : * is a NOP.
358 : *
359 : * This function is called every time we build a new descriptor. That's
360 : * because we want our Onionbalance keys to always use up-to-date
361 : * subcredentials both for the instance (ourselves) and for the onionbalance
362 : * frontend.
363 : */
364 : void
365 46 : hs_ob_refresh_keys(hs_service_t *service)
366 : {
367 46 : hs_subcredential_t *ob_subcreds = NULL;
368 46 : size_t num_subcreds;
369 :
370 46 : tor_assert(service);
371 :
372 : /* Don't do any of this if we are not configured as an OB instance */
373 46 : if (!hs_ob_service_is_instance(service)) {
374 44 : return;
375 : }
376 :
377 : /* We need both service descriptors created to make onionbalance keys.
378 : *
379 : * That's because we fetch our own (the instance's) subcredentials from our
380 : * own descriptors which should always include the latest subcredentials that
381 : * clients would use.
382 : *
383 : * This function is called with each descriptor build, so we will be
384 : * eventually be called when both descriptors are created. */
385 2 : if (!service->desc_current || !service->desc_next) {
386 : return;
387 : }
388 :
389 : /* Get a new set of subcreds */
390 2 : num_subcreds = compute_subcredentials(service, &ob_subcreds);
391 2 : if (BUG(!num_subcreds)) {
392 0 : return;
393 : }
394 :
395 : /* Delete old subcredentials if any */
396 2 : if (service->state.ob_subcreds) {
397 1 : tor_free(service->state.ob_subcreds);
398 : }
399 :
400 2 : service->state.ob_subcreds = ob_subcreds;
401 2 : service->state.n_ob_subcreds = num_subcreds;
402 : }
403 :
404 : /** Free any memory allocated by the onionblance subsystem. */
405 : void
406 278 : hs_ob_free_all(void)
407 : {
408 278 : config_mgr_free(config_options_mgr);
409 278 : }
|