Line data Source code
1 : /* Copyright (c) 2016-2021, The Tor Project, Inc. */
2 : /* See LICENSE for licensing information */
3 :
4 : /**
5 : * \file hs_intropoint.c
6 : * \brief Implement next generation introductions point functionality
7 : **/
8 :
9 : #define HS_INTROPOINT_PRIVATE
10 :
11 : #include "core/or/or.h"
12 : #include "app/config/config.h"
13 : #include "core/or/channel.h"
14 : #include "core/or/circuitlist.h"
15 : #include "core/or/circuituse.h"
16 : #include "core/or/relay.h"
17 : #include "feature/rend/rendmid.h"
18 : #include "feature/stats/rephist.h"
19 : #include "lib/crypt_ops/crypto_format.h"
20 :
21 : /* Trunnel */
22 : #include "trunnel/ed25519_cert.h"
23 : #include "trunnel/hs/cell_common.h"
24 : #include "trunnel/hs/cell_establish_intro.h"
25 : #include "trunnel/hs/cell_introduce1.h"
26 :
27 : #include "feature/hs/hs_circuitmap.h"
28 : #include "feature/hs/hs_common.h"
29 : #include "feature/hs/hs_config.h"
30 : #include "feature/hs/hs_descriptor.h"
31 : #include "feature/hs/hs_dos.h"
32 : #include "feature/hs/hs_intropoint.h"
33 :
34 : #include "core/or/or_circuit_st.h"
35 :
36 : /** Extract the authentication key from an ESTABLISH_INTRO or INTRODUCE1 using
37 : * the given <b>cell_type</b> from <b>cell</b> and place it in
38 : * <b>auth_key_out</b>. */
39 : STATIC void
40 17 : get_auth_key_from_cell(ed25519_public_key_t *auth_key_out,
41 : unsigned int cell_type, const void *cell)
42 : {
43 17 : size_t auth_key_len;
44 17 : const uint8_t *key_array;
45 :
46 17 : tor_assert(auth_key_out);
47 17 : tor_assert(cell);
48 :
49 17 : switch (cell_type) {
50 16 : case RELAY_COMMAND_ESTABLISH_INTRO:
51 : {
52 16 : const trn_cell_establish_intro_t *c_cell = cell;
53 16 : key_array = trn_cell_establish_intro_getconstarray_auth_key(c_cell);
54 16 : auth_key_len = trn_cell_establish_intro_getlen_auth_key(c_cell);
55 16 : break;
56 : }
57 1 : case RELAY_COMMAND_INTRODUCE1:
58 : {
59 1 : const trn_cell_introduce1_t *c_cell = cell;
60 1 : key_array = trn_cell_introduce1_getconstarray_auth_key(cell);
61 1 : auth_key_len = trn_cell_introduce1_getlen_auth_key(c_cell);
62 1 : break;
63 : }
64 0 : default:
65 : /* Getting here is really bad as it means we got a unknown cell type from
66 : * this file where every call has an hardcoded value. */
67 : tor_assert_unreached(); /* LCOV_EXCL_LINE */
68 : }
69 17 : tor_assert(key_array);
70 17 : tor_assert(auth_key_len == sizeof(auth_key_out->pubkey));
71 17 : memcpy(auth_key_out->pubkey, key_array, auth_key_len);
72 17 : }
73 :
74 : /** We received an ESTABLISH_INTRO <b>cell</b>. Verify its signature and MAC,
75 : * given <b>circuit_key_material</b>. Return 0 on success else -1 on error. */
76 : STATIC int
77 11 : verify_establish_intro_cell(const trn_cell_establish_intro_t *cell,
78 : const uint8_t *circuit_key_material,
79 : size_t circuit_key_material_len)
80 : {
81 : /* We only reach this function if the first byte of the cell is 0x02 which
82 : * means that auth_key_type is of ed25519 type, hence this check should
83 : * always pass. See hs_intro_received_establish_intro(). */
84 11 : if (BUG(cell->auth_key_type != TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519)) {
85 0 : return -1;
86 : }
87 :
88 : /* Make sure the auth key length is of the right size for this type. For
89 : * EXTRA safety, we check both the size of the array and the length which
90 : * must be the same. Safety first!*/
91 21 : if (trn_cell_establish_intro_getlen_auth_key(cell) != ED25519_PUBKEY_LEN ||
92 10 : trn_cell_establish_intro_get_auth_key_len(cell) != ED25519_PUBKEY_LEN) {
93 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
94 : "ESTABLISH_INTRO auth key length is invalid");
95 1 : return -1;
96 : }
97 :
98 10 : const uint8_t *msg = cell->start_cell;
99 :
100 : /* Verify the sig */
101 : {
102 10 : ed25519_signature_t sig_struct;
103 10 : const uint8_t *sig_array =
104 10 : trn_cell_establish_intro_getconstarray_sig(cell);
105 :
106 : /* Make sure the signature length is of the right size. For EXTRA safety,
107 : * we check both the size of the array and the length which must be the
108 : * same. Safety first!*/
109 19 : if (trn_cell_establish_intro_getlen_sig(cell) != sizeof(sig_struct.sig) ||
110 9 : trn_cell_establish_intro_get_sig_len(cell) != sizeof(sig_struct.sig)) {
111 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
112 : "ESTABLISH_INTRO sig len is invalid");
113 2 : return -1;
114 : }
115 : /* We are now sure that sig_len is of the right size. */
116 9 : memcpy(sig_struct.sig, sig_array, cell->sig_len);
117 :
118 9 : ed25519_public_key_t auth_key;
119 9 : get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, cell);
120 :
121 9 : const size_t sig_msg_len = cell->end_sig_fields - msg;
122 9 : int sig_mismatch = ed25519_checksig_prefixed(&sig_struct,
123 : msg, sig_msg_len,
124 : ESTABLISH_INTRO_SIG_PREFIX,
125 : &auth_key);
126 9 : if (sig_mismatch) {
127 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
128 : "ESTABLISH_INTRO signature not as expected");
129 1 : return -1;
130 : }
131 : }
132 :
133 : /* Verify the MAC */
134 : {
135 8 : const size_t auth_msg_len = cell->end_mac_fields - msg;
136 8 : uint8_t mac[DIGEST256_LEN];
137 8 : crypto_mac_sha3_256(mac, sizeof(mac),
138 : circuit_key_material, circuit_key_material_len,
139 : msg, auth_msg_len);
140 8 : if (tor_memneq(mac, cell->handshake_mac, sizeof(mac))) {
141 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
142 : "ESTABLISH_INTRO handshake_auth not as expected");
143 1 : return -1;
144 : }
145 : }
146 :
147 7 : return 0;
148 : }
149 :
150 : /** Send an INTRO_ESTABLISHED cell to <b>circ</b>. */
151 5 : MOCK_IMPL(int,
152 : hs_intro_send_intro_established_cell,(or_circuit_t *circ))
153 : {
154 5 : int ret;
155 5 : uint8_t *encoded_cell = NULL;
156 5 : ssize_t encoded_len, result_len;
157 5 : trn_cell_intro_established_t *cell;
158 5 : trn_cell_extension_t *ext;
159 :
160 5 : tor_assert(circ);
161 :
162 : /* Build the cell payload. */
163 5 : cell = trn_cell_intro_established_new();
164 5 : ext = trn_cell_extension_new();
165 5 : trn_cell_extension_set_num(ext, 0);
166 5 : trn_cell_intro_established_set_extensions(cell, ext);
167 : /* Encode the cell to binary format. */
168 5 : encoded_len = trn_cell_intro_established_encoded_len(cell);
169 5 : tor_assert(encoded_len > 0);
170 5 : encoded_cell = tor_malloc_zero(encoded_len);
171 5 : result_len = trn_cell_intro_established_encode(encoded_cell, encoded_len,
172 : cell);
173 5 : tor_assert(encoded_len == result_len);
174 :
175 5 : ret = relay_send_command_from_edge(0, TO_CIRCUIT(circ),
176 : RELAY_COMMAND_INTRO_ESTABLISHED,
177 : (char *) encoded_cell, encoded_len,
178 : NULL);
179 : /* On failure, the above function will close the circuit. */
180 5 : trn_cell_intro_established_free(cell);
181 5 : tor_free(encoded_cell);
182 5 : return ret;
183 : }
184 :
185 : /** Validate the cell DoS extension parameters. Return true iff they've been
186 : * bound check and can be used. Else return false. See proposal 305 for
187 : * details and reasons about this validation. */
188 : STATIC bool
189 10 : cell_dos_extension_parameters_are_valid(uint64_t intro2_rate_per_sec,
190 : uint64_t intro2_burst_per_sec)
191 : {
192 10 : bool ret = false;
193 :
194 : /* Check that received value is not below the minimum. Don't check if minimum
195 : is set to 0, since the param is a positive value and gcc will complain. */
196 : #if HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN > 0
197 : if (intro2_rate_per_sec < HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN) {
198 : log_fn(LOG_PROTOCOL_WARN, LD_REND,
199 : "Intro point DoS defenses rate per second is "
200 : "too small. Received value: %" PRIu64, intro2_rate_per_sec);
201 : goto end;
202 : }
203 : #endif /* HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MIN > 0 */
204 :
205 : /* Check that received value is not above maximum */
206 10 : if (intro2_rate_per_sec > HS_CONFIG_V3_DOS_DEFENSE_RATE_PER_SEC_MAX) {
207 2 : log_fn(LOG_PROTOCOL_WARN, LD_REND,
208 : "Intro point DoS defenses rate per second is "
209 : "too big. Received value: %" PRIu64, intro2_rate_per_sec);
210 2 : goto end;
211 : }
212 :
213 : /* Check that received value is not below the minimum */
214 : #if HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN > 0
215 : if (intro2_burst_per_sec < HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN) {
216 : log_fn(LOG_PROTOCOL_WARN, LD_REND,
217 : "Intro point DoS defenses burst per second is "
218 : "too small. Received value: %" PRIu64, intro2_burst_per_sec);
219 : goto end;
220 : }
221 : #endif /* HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MIN > 0 */
222 :
223 : /* Check that received value is not above maximum */
224 8 : if (intro2_burst_per_sec > HS_CONFIG_V3_DOS_DEFENSE_BURST_PER_SEC_MAX) {
225 1 : log_fn(LOG_PROTOCOL_WARN, LD_REND,
226 : "Intro point DoS defenses burst per second is "
227 : "too big. Received value: %" PRIu64, intro2_burst_per_sec);
228 1 : goto end;
229 : }
230 :
231 : /* In a rate limiting scenario, burst can never be smaller than the rate. At
232 : * best it can be equal. */
233 7 : if (intro2_burst_per_sec < intro2_rate_per_sec) {
234 2 : log_info(LD_REND, "Intro point DoS defenses burst is smaller than rate. "
235 : "Rate: %" PRIu64 " vs Burst: %" PRIu64,
236 : intro2_rate_per_sec, intro2_burst_per_sec);
237 2 : goto end;
238 : }
239 :
240 : /* Passing validation. */
241 : ret = true;
242 :
243 10 : end:
244 10 : return ret;
245 : }
246 :
247 : /** Parse the cell DoS extension and apply defenses on the given circuit if
248 : * validation passes. If the cell extension is malformed or contains unusable
249 : * values, the DoS defenses is disabled on the circuit. */
250 : static void
251 5 : handle_establish_intro_cell_dos_extension(
252 : const trn_cell_extension_field_t *field,
253 : or_circuit_t *circ)
254 : {
255 5 : ssize_t ret;
256 5 : uint64_t intro2_rate_per_sec = 0, intro2_burst_per_sec = 0;
257 5 : trn_cell_extension_dos_t *dos = NULL;
258 :
259 5 : tor_assert(field);
260 5 : tor_assert(circ);
261 :
262 5 : ret = trn_cell_extension_dos_parse(&dos,
263 : trn_cell_extension_field_getconstarray_field(field),
264 : trn_cell_extension_field_getlen_field(field));
265 5 : if (ret < 0) {
266 0 : goto end;
267 : }
268 :
269 15 : for (size_t i = 0; i < trn_cell_extension_dos_get_n_params(dos); i++) {
270 10 : const trn_cell_extension_dos_param_t *param =
271 10 : trn_cell_extension_dos_getconst_params(dos, i);
272 10 : if (BUG(param == NULL)) {
273 0 : goto end;
274 : }
275 :
276 10 : switch (trn_cell_extension_dos_param_get_type(param)) {
277 5 : case TRUNNEL_DOS_PARAM_TYPE_INTRO2_RATE_PER_SEC:
278 5 : intro2_rate_per_sec = trn_cell_extension_dos_param_get_value(param);
279 5 : break;
280 5 : case TRUNNEL_DOS_PARAM_TYPE_INTRO2_BURST_PER_SEC:
281 5 : intro2_burst_per_sec = trn_cell_extension_dos_param_get_value(param);
282 5 : break;
283 0 : default:
284 0 : goto end;
285 : }
286 : }
287 :
288 : /* At this point, the extension is valid so any values out of it implies
289 : * that it was set explicitly and thus flag the circuit that it should not
290 : * look at the consensus for that reason for the defenses' values. */
291 5 : circ->introduce2_dos_defense_explicit = 1;
292 :
293 : /* A value of 0 is valid in the sense that we accept it but we still disable
294 : * the defenses so return false. */
295 5 : if (intro2_rate_per_sec == 0 || intro2_burst_per_sec == 0) {
296 2 : log_info(LD_REND, "Intro point DoS defenses parameter set to 0. "
297 : "Disabling INTRO2 DoS defenses on circuit id %u",
298 : circ->p_circ_id);
299 2 : circ->introduce2_dos_defense_enabled = 0;
300 2 : goto end;
301 : }
302 :
303 : /* If invalid, we disable the defense on the circuit. */
304 3 : if (!cell_dos_extension_parameters_are_valid(intro2_rate_per_sec,
305 : intro2_burst_per_sec)) {
306 2 : circ->introduce2_dos_defense_enabled = 0;
307 2 : log_info(LD_REND, "Disabling INTRO2 DoS defenses on circuit id %u",
308 : circ->p_circ_id);
309 2 : goto end;
310 : }
311 :
312 : /* We passed validation, enable defenses and apply rate/burst. */
313 1 : circ->introduce2_dos_defense_enabled = 1;
314 :
315 : /* Initialize the INTRODUCE2 token bucket for the rate limiting. */
316 1 : token_bucket_ctr_init(&circ->introduce2_bucket,
317 : (uint32_t) intro2_rate_per_sec,
318 : (uint32_t) intro2_burst_per_sec,
319 1 : (uint32_t) approx_time());
320 1 : log_info(LD_REND, "Intro point DoS defenses enabled. Rate is %" PRIu64
321 : " and Burst is %" PRIu64,
322 : intro2_rate_per_sec, intro2_burst_per_sec);
323 :
324 5 : end:
325 5 : trn_cell_extension_dos_free(dos);
326 5 : return;
327 : }
328 :
329 : /** Parse every cell extension in the given ESTABLISH_INTRO cell. */
330 : static void
331 6 : handle_establish_intro_cell_extensions(
332 : const trn_cell_establish_intro_t *parsed_cell,
333 : or_circuit_t *circ)
334 : {
335 6 : const trn_cell_extension_t *extensions;
336 :
337 6 : tor_assert(parsed_cell);
338 6 : tor_assert(circ);
339 :
340 6 : extensions = trn_cell_establish_intro_getconst_extensions(parsed_cell);
341 6 : if (extensions == NULL) {
342 0 : goto end;
343 : }
344 :
345 : /* Go over all extensions. */
346 11 : for (size_t idx = 0; idx < trn_cell_extension_get_num(extensions); idx++) {
347 5 : const trn_cell_extension_field_t *field =
348 5 : trn_cell_extension_getconst_fields(extensions, idx);
349 5 : if (BUG(field == NULL)) {
350 : /* The number of extensions should match the number of fields. */
351 : break;
352 : }
353 :
354 5 : switch (trn_cell_extension_field_get_field_type(field)) {
355 5 : case TRUNNEL_CELL_EXTENSION_TYPE_DOS:
356 : /* After this, the circuit should be set for DoS defenses. */
357 5 : handle_establish_intro_cell_dos_extension(field, circ);
358 5 : break;
359 : default:
360 : /* Unknown extension. Skip over. */
361 : break;
362 : }
363 : }
364 :
365 6 : end:
366 6 : return;
367 : }
368 :
369 : /** We received an ESTABLISH_INTRO <b>parsed_cell</b> on <b>circ</b>. It's
370 : * well-formed and passed our verifications. Perform appropriate actions to
371 : * establish an intro point. */
372 : static int
373 6 : handle_verified_establish_intro_cell(or_circuit_t *circ,
374 : const trn_cell_establish_intro_t *parsed_cell)
375 : {
376 : /* Get the auth key of this intro point */
377 6 : ed25519_public_key_t auth_key;
378 6 : get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO,
379 : parsed_cell);
380 :
381 : /* Setup INTRODUCE2 defenses on the circuit. Must be done before parsing the
382 : * cell extension that can possibly change the defenses' values. */
383 6 : hs_dos_setup_default_intro2_defenses(circ);
384 :
385 : /* Handle cell extension if any. */
386 6 : handle_establish_intro_cell_extensions(parsed_cell, circ);
387 :
388 : /* Then notify the hidden service that the intro point is established by
389 : sending an INTRO_ESTABLISHED cell */
390 6 : if (hs_intro_send_intro_established_cell(circ)) {
391 0 : log_warn(LD_PROTOCOL, "Couldn't send INTRO_ESTABLISHED cell.");
392 0 : return -1;
393 : }
394 :
395 : /* Associate intro point auth key with this circuit. */
396 6 : hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
397 : /* Repurpose this circuit into an intro circuit. */
398 6 : circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
399 :
400 6 : return 0;
401 : }
402 :
403 : /** We just received an ESTABLISH_INTRO cell in <b>circ</b> with payload in
404 : * <b>request</b>. Handle it by making <b>circ</b> an intro circuit. Return 0
405 : * if everything went well, or -1 if there were errors. */
406 : static int
407 11 : handle_establish_intro(or_circuit_t *circ, const uint8_t *request,
408 : size_t request_len)
409 : {
410 11 : int cell_ok, retval = -1;
411 11 : trn_cell_establish_intro_t *parsed_cell = NULL;
412 :
413 11 : tor_assert(circ);
414 11 : tor_assert(request);
415 :
416 11 : log_info(LD_REND, "Received an ESTABLISH_INTRO request on circuit %" PRIu32,
417 : circ->p_circ_id);
418 :
419 : /* Check that the circuit is in shape to become an intro point */
420 11 : if (!hs_intro_circuit_is_suitable_for_establish_intro(circ)) {
421 1 : goto err;
422 : }
423 :
424 : /* Parse the cell */
425 10 : ssize_t parsing_result = trn_cell_establish_intro_parse(&parsed_cell,
426 : request, request_len);
427 10 : if (parsing_result < 0) {
428 0 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
429 : "Rejecting %s ESTABLISH_INTRO cell.",
430 : parsing_result == -1 ? "invalid" : "truncated");
431 0 : goto err;
432 : }
433 :
434 20 : cell_ok = verify_establish_intro_cell(parsed_cell,
435 10 : (uint8_t *) circ->rend_circ_nonce,
436 : sizeof(circ->rend_circ_nonce));
437 10 : if (cell_ok < 0) {
438 4 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
439 : "Failed to verify ESTABLISH_INTRO cell.");
440 4 : goto err;
441 : }
442 :
443 : /* This cell is legit. Take the appropriate actions. */
444 6 : cell_ok = handle_verified_establish_intro_cell(circ, parsed_cell);
445 6 : if (cell_ok < 0) {
446 0 : goto err;
447 : }
448 :
449 : /* We are done! */
450 6 : retval = 0;
451 6 : goto done;
452 :
453 5 : err:
454 : /* When sending the intro establish ack, on error the circuit can be marked
455 : * as closed so avoid a double close. */
456 5 : if (!TO_CIRCUIT(circ)->marked_for_close) {
457 5 : circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
458 : }
459 :
460 0 : done:
461 11 : trn_cell_establish_intro_free(parsed_cell);
462 11 : return retval;
463 : }
464 :
465 : /** Return True if circuit is suitable for being an intro circuit. */
466 : static int
467 17 : circuit_is_suitable_intro_point(const or_circuit_t *circ,
468 : const char *log_cell_type_str)
469 : {
470 17 : tor_assert(circ);
471 17 : tor_assert(log_cell_type_str);
472 :
473 : /* Basic circuit state sanity checks. */
474 17 : if (circ->base_.purpose != CIRCUIT_PURPOSE_OR) {
475 3 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
476 : "Rejecting %s on non-OR circuit.", log_cell_type_str);
477 3 : return 0;
478 : }
479 :
480 14 : if (circ->base_.n_chan) {
481 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
482 : "Rejecting %s on non-edge circuit.", log_cell_type_str);
483 1 : return 0;
484 : }
485 :
486 : /* Suitable. */
487 : return 1;
488 : }
489 :
490 : /** Return True if circuit is suitable for being service-side intro circuit. */
491 : int
492 11 : hs_intro_circuit_is_suitable_for_establish_intro(const or_circuit_t *circ)
493 : {
494 11 : return circuit_is_suitable_intro_point(circ, "ESTABLISH_INTRO");
495 : }
496 :
497 : /** We just received an ESTABLISH_INTRO cell in <b>circ</b>. Pass it to the
498 : * appropriate handler. */
499 : int
500 13 : hs_intro_received_establish_intro(or_circuit_t *circ, const uint8_t *request,
501 : size_t request_len)
502 : {
503 13 : tor_assert(circ);
504 13 : tor_assert(request);
505 :
506 13 : if (request_len == 0) {
507 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Empty ESTABLISH_INTRO cell.");
508 1 : goto err;
509 : }
510 :
511 : /* Using the first byte of the cell, figure out the version of
512 : * ESTABLISH_INTRO and pass it to the appropriate cell handler */
513 12 : const uint8_t first_byte = request[0];
514 12 : switch (first_byte) {
515 0 : case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY0:
516 : case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_LEGACY1:
517 : /* Likely version 2 onion service which is now obsolete. Avoid a
518 : * protocol warning considering they still exists on the network. */
519 0 : goto err;
520 11 : case TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519:
521 11 : return handle_establish_intro(circ, request, request_len);
522 1 : default:
523 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
524 : "Unrecognized AUTH_KEY_TYPE %u.", first_byte);
525 1 : goto err;
526 : }
527 :
528 2 : err:
529 2 : circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
530 2 : return -1;
531 : }
532 :
533 : /** Send an INTRODUCE_ACK cell onto the circuit <b>circ</b> with the status
534 : * value in <b>status</b>. Depending on the status, it can be ACK or a NACK.
535 : * Return 0 on success else a negative value on error which will close the
536 : * circuit. */
537 : static int
538 2 : send_introduce_ack_cell(or_circuit_t *circ, uint16_t status)
539 : {
540 2 : int ret = -1;
541 2 : uint8_t *encoded_cell = NULL;
542 2 : ssize_t encoded_len, result_len;
543 2 : trn_cell_introduce_ack_t *cell;
544 2 : trn_cell_extension_t *ext;
545 :
546 2 : tor_assert(circ);
547 :
548 : /* Setup the INTRODUCE_ACK cell. We have no extensions so the N_EXTENSIONS
549 : * field is set to 0 by default with a new object. */
550 2 : cell = trn_cell_introduce_ack_new();
551 2 : ret = trn_cell_introduce_ack_set_status(cell, status);
552 : /* We have no cell extensions in an INTRODUCE_ACK cell. */
553 2 : ext = trn_cell_extension_new();
554 2 : trn_cell_extension_set_num(ext, 0);
555 2 : trn_cell_introduce_ack_set_extensions(cell, ext);
556 : /* A wrong status is a very bad code flow error as this value is controlled
557 : * by the code in this file and not an external input. This means we use a
558 : * code that is not known by the trunnel ABI. */
559 2 : tor_assert(ret == 0);
560 : /* Encode the payload. We should never fail to get the encoded length. */
561 2 : encoded_len = trn_cell_introduce_ack_encoded_len(cell);
562 2 : tor_assert(encoded_len > 0);
563 2 : encoded_cell = tor_malloc_zero(encoded_len);
564 2 : result_len = trn_cell_introduce_ack_encode(encoded_cell, encoded_len, cell);
565 2 : tor_assert(encoded_len == result_len);
566 :
567 2 : ret = relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ),
568 : RELAY_COMMAND_INTRODUCE_ACK,
569 : (char *) encoded_cell, encoded_len,
570 : NULL);
571 : /* On failure, the above function will close the circuit. */
572 2 : trn_cell_introduce_ack_free(cell);
573 2 : tor_free(encoded_cell);
574 2 : return ret;
575 : }
576 :
577 : /** Validate a parsed INTRODUCE1 <b>cell</b>. Return 0 if valid or else a
578 : * negative value for an invalid cell that should be NACKed. */
579 : STATIC int
580 10 : validate_introduce1_parsed_cell(const trn_cell_introduce1_t *cell)
581 : {
582 10 : size_t legacy_key_id_len;
583 10 : const uint8_t *legacy_key_id;
584 :
585 10 : tor_assert(cell);
586 :
587 : /* This code path SHOULD NEVER be reached if the cell is a legacy type so
588 : * safety net here. The legacy ID must be zeroes in this case. */
589 10 : legacy_key_id_len = trn_cell_introduce1_getlen_legacy_key_id(cell);
590 10 : legacy_key_id = trn_cell_introduce1_getconstarray_legacy_key_id(cell);
591 10 : if (BUG(!fast_mem_is_zero((char *) legacy_key_id, legacy_key_id_len))) {
592 0 : goto invalid;
593 : }
594 :
595 : /* The auth key of an INTRODUCE1 should be of type ed25519 thus leading to a
596 : * known fixed length as well. */
597 10 : if (trn_cell_introduce1_get_auth_key_type(cell) !=
598 : TRUNNEL_HS_INTRO_AUTH_KEY_TYPE_ED25519) {
599 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
600 : "Rejecting invalid INTRODUCE1 cell auth key type. "
601 : "Responding with NACK.");
602 1 : goto invalid;
603 : }
604 16 : if (trn_cell_introduce1_get_auth_key_len(cell) != ED25519_PUBKEY_LEN ||
605 7 : trn_cell_introduce1_getlen_auth_key(cell) != ED25519_PUBKEY_LEN) {
606 3 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
607 : "Rejecting invalid INTRODUCE1 cell auth key length. "
608 : "Responding with NACK.");
609 3 : goto invalid;
610 : }
611 6 : if (trn_cell_introduce1_getlen_encrypted(cell) == 0) {
612 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
613 : "Rejecting invalid INTRODUCE1 cell encrypted length. "
614 : "Responding with NACK.");
615 1 : goto invalid;
616 : }
617 :
618 : return 0;
619 : invalid:
620 : return -1;
621 : }
622 :
623 : /** We just received a non legacy INTRODUCE1 cell on <b>client_circ</b> with
624 : * the payload in <b>request</b> of size <b>request_len</b>. Return 0 if
625 : * everything went well, or -1 if an error occurred. This function is in charge
626 : * of sending back an INTRODUCE_ACK cell and will close client_circ on error.
627 : */
628 : STATIC int
629 2 : handle_introduce1(or_circuit_t *client_circ, const uint8_t *request,
630 : size_t request_len)
631 : {
632 2 : int ret = -1;
633 2 : or_circuit_t *service_circ;
634 2 : trn_cell_introduce1_t *parsed_cell;
635 2 : uint16_t status = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
636 :
637 2 : tor_assert(client_circ);
638 2 : tor_assert(request);
639 :
640 : /* Parse cell. Note that we can only parse the non encrypted section for
641 : * which we'll use the authentication key to find the service introduction
642 : * circuit and relay the cell on it. */
643 2 : ssize_t cell_size = trn_cell_introduce1_parse(&parsed_cell, request,
644 : request_len);
645 2 : if (cell_size < 0) {
646 2 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
647 : "Rejecting %s INTRODUCE1 cell. Responding with NACK.",
648 : cell_size == -1 ? "invalid" : "truncated");
649 : /* Inform client that the INTRODUCE1 has a bad format. */
650 1 : status = TRUNNEL_HS_INTRO_ACK_STATUS_BAD_FORMAT;
651 1 : goto send_ack;
652 : }
653 :
654 : /* Once parsed validate the cell format. */
655 1 : if (validate_introduce1_parsed_cell(parsed_cell) < 0) {
656 : /* Inform client that the INTRODUCE1 has bad format. */
657 0 : status = TRUNNEL_HS_INTRO_ACK_STATUS_BAD_FORMAT;
658 0 : goto send_ack;
659 : }
660 :
661 : /* Find introduction circuit through our circuit map. */
662 : {
663 1 : ed25519_public_key_t auth_key;
664 1 : get_auth_key_from_cell(&auth_key, RELAY_COMMAND_INTRODUCE1, parsed_cell);
665 1 : service_circ = hs_circuitmap_get_intro_circ_v3_relay_side(&auth_key);
666 1 : if (service_circ == NULL) {
667 0 : char b64_key[ED25519_BASE64_LEN + 1];
668 0 : ed25519_public_to_base64(b64_key, &auth_key);
669 0 : log_info(LD_REND, "No intro circuit found for INTRODUCE1 cell "
670 : "with auth key %s from circuit %" PRIu32 ". "
671 : "Responding with NACK.",
672 : safe_str(b64_key), client_circ->p_circ_id);
673 : /* Inform the client that we don't know the requested service ID. */
674 0 : status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
675 0 : goto send_ack;
676 : }
677 : }
678 :
679 : /* Before sending, lets make sure this cell can be sent on the service
680 : * circuit asking the DoS defenses. */
681 1 : if (!hs_dos_can_send_intro2(service_circ)) {
682 0 : char *msg;
683 0 : static ratelim_t rlimit = RATELIM_INIT(5 * 60);
684 0 : if ((msg = rate_limit_log(&rlimit, approx_time()))) {
685 0 : log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v3 cell due to DoS "
686 : "limitations. Sending NACK to client.");
687 0 : tor_free(msg);
688 : }
689 0 : status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
690 0 : goto send_ack;
691 : }
692 :
693 : /* Relay the cell to the service on its intro circuit with an INTRODUCE2
694 : * cell which is the same exact payload. */
695 1 : if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ),
696 : RELAY_COMMAND_INTRODUCE2,
697 : (char *) request, request_len, NULL)) {
698 0 : log_warn(LD_PROTOCOL, "Unable to send INTRODUCE2 cell to the service.");
699 : /* Inform the client that we can't relay the cell. Use the unknown ID
700 : * status code since it means that we do not know the service. */
701 0 : status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
702 0 : goto send_ack;
703 : }
704 :
705 : /* Success! Send an INTRODUCE_ACK success status onto the client circuit. */
706 : status = TRUNNEL_HS_INTRO_ACK_STATUS_SUCCESS;
707 : ret = 0;
708 :
709 2 : send_ack:
710 : /* Send INTRODUCE_ACK or INTRODUCE_NACK to client */
711 2 : if (send_introduce_ack_cell(client_circ, status) < 0) {
712 0 : log_warn(LD_PROTOCOL, "Unable to send an INTRODUCE ACK status %d "
713 : "to client.", status);
714 : /* Circuit has been closed on failure of transmission. */
715 0 : goto done;
716 : }
717 2 : done:
718 2 : trn_cell_introduce1_free(parsed_cell);
719 2 : return ret;
720 : }
721 :
722 : /** Return true iff the circuit <b>circ</b> is suitable for receiving an
723 : * INTRODUCE1 cell. */
724 : STATIC int
725 6 : circuit_is_suitable_for_introduce1(const or_circuit_t *circ)
726 : {
727 6 : tor_assert(circ);
728 :
729 : /* Is this circuit an intro point circuit? */
730 6 : if (!circuit_is_suitable_intro_point(circ, "INTRODUCE1")) {
731 : return 0;
732 : }
733 :
734 3 : if (circ->already_received_introduce1) {
735 1 : log_fn(LOG_PROTOCOL_WARN, LD_REND,
736 : "Blocking multiple introductions on the same circuit. "
737 : "Someone might be trying to attack a hidden service through "
738 : "this relay.");
739 1 : return 0;
740 : }
741 :
742 : /* Disallow single hop client circuit. */
743 2 : if (circ->p_chan && channel_is_client(circ->p_chan)) {
744 0 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
745 : "Single hop client was rejected while trying to introduce. "
746 : "Closing circuit.");
747 0 : return 0;
748 : }
749 :
750 : return 1;
751 : }
752 :
753 : /** We just received an INTRODUCE1 cell on <b>circ</b>. Figure out which type
754 : * it is and pass it to the appropriate handler. Return 0 on success else a
755 : * negative value and the circuit is closed. */
756 : int
757 2 : hs_intro_received_introduce1(or_circuit_t *circ, const uint8_t *request,
758 : size_t request_len)
759 : {
760 2 : tor_assert(circ);
761 2 : tor_assert(request);
762 :
763 : /* A cell that can't hold a DIGEST_LEN is invalid. */
764 2 : if (request_len < DIGEST_LEN) {
765 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Invalid INTRODUCE1 cell length.");
766 1 : goto err;
767 : }
768 :
769 : /* Make sure we have a circuit that can have an INTRODUCE1 cell on it. */
770 1 : if (!circuit_is_suitable_for_introduce1(circ)) {
771 : /* We do not send a NACK because the circuit is not suitable for any kind
772 : * of response or transmission as it's a violation of the protocol. */
773 0 : goto err;
774 : }
775 : /* Mark the circuit that we got this cell. None are allowed after this as a
776 : * DoS mitigation since one circuit with one client can hammer a service. */
777 1 : circ->already_received_introduce1 = 1;
778 :
779 : /* Handle the cell. */
780 1 : return handle_introduce1(circ, request, request_len);
781 :
782 1 : err:
783 1 : circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_TORPROTOCOL);
784 1 : return -1;
785 : }
786 :
787 : /** Clear memory allocated by the given intropoint object ip (but don't free
788 : * the object itself). */
789 : void
790 24 : hs_intropoint_clear(hs_intropoint_t *ip)
791 : {
792 24 : if (ip == NULL) {
793 : return;
794 : }
795 24 : tor_cert_free(ip->auth_key_cert);
796 51 : SMARTLIST_FOREACH(ip->link_specifiers, link_specifier_t *, ls,
797 : link_specifier_free(ls));
798 24 : smartlist_free(ip->link_specifiers);
799 24 : memset(ip, 0, sizeof(hs_intropoint_t));
800 : }
|