mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
hs: Limit the amount of relayed INTRODUCE2
This commit add the hs_dos.{c|h} file that has the purpose of having the anti-DoS code for onion services. At this commit, it only has one which is a function that decides if an INTRODUCE2 can be sent on the given introduction service circuit (S<->IP) using a simple token bucket. The rate per second is 25 and allowed burst to 200. Basic defenses on #15516. Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
4ee65a6f87
commit
9f738be893
@ -117,6 +117,7 @@ LIBTOR_APP_A_SOURCES = \
|
|||||||
src/feature/hs/hs_config.c \
|
src/feature/hs/hs_config.c \
|
||||||
src/feature/hs/hs_control.c \
|
src/feature/hs/hs_control.c \
|
||||||
src/feature/hs/hs_descriptor.c \
|
src/feature/hs/hs_descriptor.c \
|
||||||
|
src/feature/hs/hs_dos.c \
|
||||||
src/feature/hs/hs_ident.c \
|
src/feature/hs/hs_ident.c \
|
||||||
src/feature/hs/hs_intropoint.c \
|
src/feature/hs/hs_intropoint.c \
|
||||||
src/feature/hs/hs_service.c \
|
src/feature/hs/hs_service.c \
|
||||||
@ -374,6 +375,7 @@ noinst_HEADERS += \
|
|||||||
src/feature/hs/hs_config.h \
|
src/feature/hs/hs_config.h \
|
||||||
src/feature/hs/hs_control.h \
|
src/feature/hs/hs_control.h \
|
||||||
src/feature/hs/hs_descriptor.h \
|
src/feature/hs/hs_descriptor.h \
|
||||||
|
src/feature/hs/hs_dos.h \
|
||||||
src/feature/hs/hs_ident.h \
|
src/feature/hs/hs_ident.h \
|
||||||
src/feature/hs/hs_intropoint.h \
|
src/feature/hs/hs_intropoint.h \
|
||||||
src/feature/hs/hs_service.h \
|
src/feature/hs/hs_service.h \
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "core/or/circuit_st.h"
|
#include "core/or/circuit_st.h"
|
||||||
#include "core/or/crypt_path_st.h"
|
#include "core/or/crypt_path_st.h"
|
||||||
|
|
||||||
|
#include "lib/evloop/token_bucket.h"
|
||||||
|
|
||||||
struct onion_queue_t;
|
struct onion_queue_t;
|
||||||
|
|
||||||
/** An or_circuit_t holds information needed to implement a circuit at an
|
/** An or_circuit_t holds information needed to implement a circuit at an
|
||||||
@ -69,6 +71,11 @@ struct or_circuit_t {
|
|||||||
* exit-ward queues of this circuit; reset every time when writing
|
* exit-ward queues of this circuit; reset every time when writing
|
||||||
* buffer stats to disk. */
|
* buffer stats to disk. */
|
||||||
uint64_t total_cell_waiting_time;
|
uint64_t total_cell_waiting_time;
|
||||||
|
|
||||||
|
/** INTRODUCE2 cell bucket controlling how much can go on this circuit. Only
|
||||||
|
* used if this is a service introduction circuit at the intro point
|
||||||
|
* (purpose = CIRCUIT_PURPOSE_INTRO_POINT). */
|
||||||
|
token_bucket_ctr_t introduce2_bucket;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* !defined(OR_CIRCUIT_ST_H) */
|
#endif /* !defined(OR_CIRCUIT_ST_H) */
|
||||||
|
60
src/feature/hs/hs_dos.c
Normal file
60
src/feature/hs/hs_dos.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* Copyright (c) 2019, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file hs_dos.c
|
||||||
|
* \brief Implement denial of service mitigation for the onion service
|
||||||
|
* subsystem.
|
||||||
|
*
|
||||||
|
* This module defenses:
|
||||||
|
*
|
||||||
|
* - Introduction Rate Limiting: If enabled by the consensus, an introduction
|
||||||
|
* point will rate limit client introduction towards the service (INTRODUCE2
|
||||||
|
* cells). It uses a token bucket model with a rate and burst per second.
|
||||||
|
*
|
||||||
|
* Proposal 305 will expand this module by allowing an operator to define
|
||||||
|
* these values into the ESTABLISH_INTRO cell. Not yet implemented.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#define HS_DOS_PRIVATE
|
||||||
|
|
||||||
|
#include "core/or/circuitlist.h"
|
||||||
|
|
||||||
|
#include "hs_dos.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public API.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Return true iff an INTRODUCE2 cell can be sent on the given service
|
||||||
|
* introduction circuit. */
|
||||||
|
bool
|
||||||
|
hs_dos_can_send_intro2(or_circuit_t *s_intro_circ)
|
||||||
|
{
|
||||||
|
tor_assert(s_intro_circ);
|
||||||
|
|
||||||
|
/* Should not happen but if so, scream loudly. */
|
||||||
|
if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called just after we got a valid and parsed INTRODUCE1 cell. The
|
||||||
|
* service has been found and we have its introduction circuit.
|
||||||
|
*
|
||||||
|
* First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented
|
||||||
|
* because we are about to send or not the cell we just got. Finally,
|
||||||
|
* evaluate if we can send it based on our token bucket state. */
|
||||||
|
|
||||||
|
/* Refill INTRODUCE2 bucket. */
|
||||||
|
token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket,
|
||||||
|
(uint32_t) approx_time());
|
||||||
|
|
||||||
|
/* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't
|
||||||
|
* underflow else we end up with a too big of a bucket. */
|
||||||
|
if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) {
|
||||||
|
token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, we can send a new INTRODUCE2 if there are still tokens. */
|
||||||
|
return token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0;
|
||||||
|
}
|
44
src/feature/hs/hs_dos.h
Normal file
44
src/feature/hs/hs_dos.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* Copyright (c) 2019, The Tor Project, Inc. */
|
||||||
|
/* See LICENSE for licensing information */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \file hs_dos.h
|
||||||
|
* \brief Header file containing denial of service defenses for the HS
|
||||||
|
* subsystem for all versions.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef TOR_HS_DOS_H
|
||||||
|
#define TOR_HS_DOS_H
|
||||||
|
|
||||||
|
#include "core/or/or_circuit_st.h"
|
||||||
|
|
||||||
|
#include "lib/evloop/token_bucket.h"
|
||||||
|
|
||||||
|
#define HS_DOS_INTRODUCE_CELL_RATE_PER_SEC 25
|
||||||
|
#define HS_DOS_INTRODUCE_CELL_BURST_PER_SEC 200
|
||||||
|
|
||||||
|
bool hs_dos_can_send_intro2(or_circuit_t *s_intro_circ);
|
||||||
|
|
||||||
|
/* Return the INTRODUCE2 cell rate per second. */
|
||||||
|
static inline
|
||||||
|
uint32_t hs_dos_get_intro2_rate(void)
|
||||||
|
{
|
||||||
|
return HS_DOS_INTRODUCE_CELL_RATE_PER_SEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the INTRODUCE2 cell burst per second. */
|
||||||
|
static inline
|
||||||
|
uint32_t hs_dos_get_intro2_burst(void)
|
||||||
|
{
|
||||||
|
return HS_DOS_INTRODUCE_CELL_BURST_PER_SEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HS_DOS_PRIVATE
|
||||||
|
|
||||||
|
#ifdef TOR_UNIT_TESTS
|
||||||
|
|
||||||
|
#endif /* define(TOR_UNIT_TESTS) */
|
||||||
|
|
||||||
|
#endif /* defined(HS_DOS_PRIVATE) */
|
||||||
|
|
||||||
|
#endif /* !defined(TOR_HS_DOS_H) */
|
@ -25,9 +25,10 @@
|
|||||||
#include "trunnel/hs/cell_introduce1.h"
|
#include "trunnel/hs/cell_introduce1.h"
|
||||||
|
|
||||||
#include "feature/hs/hs_circuitmap.h"
|
#include "feature/hs/hs_circuitmap.h"
|
||||||
#include "feature/hs/hs_descriptor.h"
|
|
||||||
#include "feature/hs/hs_intropoint.h"
|
|
||||||
#include "feature/hs/hs_common.h"
|
#include "feature/hs/hs_common.h"
|
||||||
|
#include "feature/hs/hs_descriptor.h"
|
||||||
|
#include "feature/hs/hs_dos.h"
|
||||||
|
#include "feature/hs/hs_intropoint.h"
|
||||||
|
|
||||||
#include "core/or/or_circuit_st.h"
|
#include "core/or/or_circuit_st.h"
|
||||||
|
|
||||||
@ -203,6 +204,9 @@ handle_verified_establish_intro_cell(or_circuit_t *circ,
|
|||||||
hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
|
hs_circuitmap_register_intro_circ_v3_relay_side(circ, &auth_key);
|
||||||
/* Repurpose this circuit into an intro circuit. */
|
/* Repurpose this circuit into an intro circuit. */
|
||||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT);
|
||||||
|
/* Initialize the INTRODUCE2 token bucket for the rate limiting. */
|
||||||
|
token_bucket_ctr_init(&circ->introduce2_bucket, hs_dos_get_intro2_rate(),
|
||||||
|
hs_dos_get_intro2_burst(), (uint32_t) approx_time());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -481,6 +485,20 @@ handle_introduce1(or_circuit_t *client_circ, const uint8_t *request,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Before sending, lets make sure this cell can be sent on the service
|
||||||
|
* circuit asking the DoS defenses. */
|
||||||
|
if (!hs_dos_can_send_intro2(service_circ)) {
|
||||||
|
char *msg;
|
||||||
|
static ratelim_t rlimit = RATELIM_INIT(5 * 60);
|
||||||
|
if ((msg = rate_limit_log(&rlimit, approx_time()))) {
|
||||||
|
log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v3 cell due to DoS "
|
||||||
|
"limitations. Sending NACK to client.");
|
||||||
|
tor_free(msg);
|
||||||
|
}
|
||||||
|
status = TRUNNEL_HS_INTRO_ACK_STATUS_UNKNOWN_ID;
|
||||||
|
goto send_ack;
|
||||||
|
}
|
||||||
|
|
||||||
/* Relay the cell to the service on its intro circuit with an INTRODUCE2
|
/* Relay the cell to the service on its intro circuit with an INTRODUCE2
|
||||||
* cell which is the same exact payload. */
|
* cell which is the same exact payload. */
|
||||||
if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ),
|
if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(service_circ),
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "feature/rend/rendmid.h"
|
#include "feature/rend/rendmid.h"
|
||||||
#include "feature/stats/rephist.h"
|
#include "feature/stats/rephist.h"
|
||||||
#include "feature/hs/hs_circuitmap.h"
|
#include "feature/hs/hs_circuitmap.h"
|
||||||
|
#include "feature/hs/hs_dos.h"
|
||||||
#include "feature/hs/hs_intropoint.h"
|
#include "feature/hs/hs_intropoint.h"
|
||||||
|
|
||||||
#include "core/or/or_circuit_st.h"
|
#include "core/or/or_circuit_st.h"
|
||||||
@ -180,6 +181,14 @@ rend_mid_introduce_legacy(or_circuit_t *circ, const uint8_t *request,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Before sending, lets make sure this cell can be sent on the service
|
||||||
|
* circuit asking the DoS defenses. */
|
||||||
|
if (!hs_dos_can_send_intro2(intro_circ)) {
|
||||||
|
log_info(LD_PROTOCOL, "Can't relay INTRODUCE1 v2 cell due to DoS "
|
||||||
|
"limitations. Sending NACK to client.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
log_info(LD_REND,
|
log_info(LD_REND,
|
||||||
"Sending introduction request for service %s "
|
"Sending introduction request for service %s "
|
||||||
"from circ %u to circ %u",
|
"from circ %u to circ %u",
|
||||||
|
@ -119,6 +119,8 @@ helper_create_intro_circuit(void)
|
|||||||
or_circuit_t *circ = or_circuit_new(0, NULL);
|
or_circuit_t *circ = or_circuit_new(0, NULL);
|
||||||
tt_assert(circ);
|
tt_assert(circ);
|
||||||
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
|
circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR);
|
||||||
|
token_bucket_ctr_init(&circ->introduce2_bucket, 100, 100,
|
||||||
|
(uint32_t) approx_time());
|
||||||
done:
|
done:
|
||||||
return circ;
|
return circ;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user