mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Merge remote-tracking branch 'yawning/bug16052a_027'
This commit is contained in:
commit
eb7f4d0059
5
changes/feature16052
Normal file
5
changes/feature16052
Normal file
@ -0,0 +1,5 @@
|
||||
o Minor features (hidden service):
|
||||
- Add the new options "HiddenServiceMaxStreams" and
|
||||
"HiddenServiceMaxStreamsCloseCircuit" to allow hidden services to limit
|
||||
the maximum number of simultaneous streams per circuit, and optionally
|
||||
tear down the circuit when the limit is exceeded. Part of ticket 16052.
|
@ -2149,6 +2149,16 @@ The following options are used to configure a hidden service.
|
||||
not an authorization mechanism; it is instead meant to be a mild
|
||||
inconvenience to port-scanners.) (Default: 0)
|
||||
|
||||
[[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__::
|
||||
The maximum number of simultaneous streams (connections) per rendezvous
|
||||
circuit. (Setting this to 0 will allow an unlimited number of simultanous
|
||||
streams.) (Default: 0)
|
||||
|
||||
[[HiddenServiceMaxStreamsCloseCircuit]] **HiddenServiceMaxStreamsCloseCircuit** **0**|**1**::
|
||||
If set to 1, then exceeding **HiddenServiceMaxStreams** will cause the
|
||||
offending rendezvous circuit to be torn down, as opposed to stream creation
|
||||
requests that exceed the limit being silently ignored. (Default: 0)
|
||||
|
||||
[[RendPostPeriod]] **RendPostPeriod** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
|
||||
Every time the specified period elapses, Tor uploads any rendezvous
|
||||
service descriptors to the directory servers. This information is also
|
||||
|
@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
|
||||
|
||||
if (CIRCUIT_IS_ORIGIN(circ)) {
|
||||
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
|
||||
int removed = 0;
|
||||
if (conn == origin_circ->p_streams) {
|
||||
origin_circ->p_streams = conn->next_stream;
|
||||
return;
|
||||
removed = 1;
|
||||
} else {
|
||||
for (prevconn = origin_circ->p_streams;
|
||||
prevconn && prevconn->next_stream && prevconn->next_stream != conn;
|
||||
prevconn = prevconn->next_stream)
|
||||
;
|
||||
if (prevconn && prevconn->next_stream) {
|
||||
prevconn->next_stream = conn->next_stream;
|
||||
removed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (prevconn = origin_circ->p_streams;
|
||||
prevconn && prevconn->next_stream && prevconn->next_stream != conn;
|
||||
prevconn = prevconn->next_stream)
|
||||
;
|
||||
if (prevconn && prevconn->next_stream) {
|
||||
prevconn->next_stream = conn->next_stream;
|
||||
if (removed) {
|
||||
/* If the stream was removed, and it was a rend stream, decrement the
|
||||
* number of streams on the circuit associated with the rend service.
|
||||
*/
|
||||
if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
|
||||
tor_assert(origin_circ->rend_data);
|
||||
origin_circ->rend_data->nr_streams--;
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
|
||||
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
|
||||
VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
|
||||
VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL),
|
||||
VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
|
||||
VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
|
||||
V(HiddenServiceStatistics, BOOL, "0"),
|
||||
V(HidServAuth, LINELIST, NULL),
|
||||
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
|
||||
|
@ -2860,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
|
||||
origin_circ->p_streams = n_stream;
|
||||
assert_circuit_ok(circ);
|
||||
|
||||
origin_circ->rend_data->nr_streams++;
|
||||
|
||||
connection_exit_connect(n_stream);
|
||||
|
||||
/* For path bias: This circuit was used successfully */
|
||||
|
@ -3566,9 +3566,12 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
smartlist_t *port_cfgs = smartlist_new();
|
||||
int discard_pk = 0;
|
||||
int detach = 0;
|
||||
int max_streams = 0;
|
||||
int max_streams_close_circuit = 0;
|
||||
for (size_t i = 1; i < arg_len; i++) {
|
||||
static const char *port_prefix = "Port=";
|
||||
static const char *flags_prefix = "Flags=";
|
||||
static const char *max_s_prefix = "MaxStreams=";
|
||||
|
||||
const char *arg = smartlist_get(args, i);
|
||||
if (!strcasecmpstart(arg, port_prefix)) {
|
||||
@ -3582,15 +3585,27 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
goto out;
|
||||
}
|
||||
smartlist_add(port_cfgs, cfg);
|
||||
} else if (!strcasecmpstart(arg, max_s_prefix)) {
|
||||
/* "MaxStreams=[0..65535]". */
|
||||
const char *max_s_str = arg + strlen(max_s_prefix);
|
||||
int ok = 0;
|
||||
max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL);
|
||||
if (!ok) {
|
||||
connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n");
|
||||
goto out;
|
||||
}
|
||||
} else if (!strcasecmpstart(arg, flags_prefix)) {
|
||||
/* "Flags=Flag[,Flag]", where Flag can be:
|
||||
* * 'DiscardPK' - If tor generates the keypair, do not include it in
|
||||
* the response.
|
||||
* * 'Detach' - Do not tie this onion service to any particular control
|
||||
* connection.
|
||||
* * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
|
||||
* exceeded.
|
||||
*/
|
||||
static const char *discard_flag = "DiscardPK";
|
||||
static const char *detach_flag = "Detach";
|
||||
static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
|
||||
|
||||
smartlist_t *flags = smartlist_new();
|
||||
int bad = 0;
|
||||
@ -3607,6 +3622,8 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
discard_pk = 1;
|
||||
} else if (!strcasecmp(flag, detach_flag)) {
|
||||
detach = 1;
|
||||
} else if (!strcasecmp(flag, max_s_close_flag)) {
|
||||
max_streams_close_circuit = 1;
|
||||
} else {
|
||||
connection_printf_to_buf(conn,
|
||||
"512 Invalid 'Flags' argument: %s\r\n",
|
||||
@ -3652,7 +3669,9 @@ handle_control_add_onion(control_connection_t *conn,
|
||||
* regardless of success/failure.
|
||||
*/
|
||||
char *service_id = NULL;
|
||||
int ret = rend_service_add_ephemeral(pk, port_cfgs, &service_id);
|
||||
int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
|
||||
max_streams_close_circuit,
|
||||
&service_id);
|
||||
port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
|
||||
switch (ret) {
|
||||
case RSAE_OKAY:
|
||||
|
@ -818,6 +818,9 @@ typedef struct rend_data_t {
|
||||
/** List of HSDir fingerprints on which this request has been sent to.
|
||||
* This contains binary identity digest of the directory. */
|
||||
smartlist_t *hsdirs_fp;
|
||||
|
||||
/** Number of streams associated with this rendezvous circuit. */
|
||||
int nr_streams;
|
||||
} rend_data_t;
|
||||
|
||||
/** Time interval for tracking replays of DH public keys received in
|
||||
|
@ -147,6 +147,13 @@ typedef struct rend_service_t {
|
||||
/** If true, we don't close circuits for making requests to unsupported
|
||||
* ports. */
|
||||
int allow_unknown_ports;
|
||||
/** The maximum number of simultanious streams-per-circuit that are allowed
|
||||
* to be established, or 0 if no limit is set.
|
||||
*/
|
||||
int max_streams_per_circuit;
|
||||
/** If true, we close circuits that exceed the max_streams_per_circuit
|
||||
* limit. */
|
||||
int max_streams_close_circuit;
|
||||
} rend_service_t;
|
||||
|
||||
/** Returns a escaped string representation of the service, <b>s</b>.
|
||||
@ -259,6 +266,23 @@ rend_add_service(rend_service_t *service)
|
||||
|
||||
service->intro_nodes = smartlist_new();
|
||||
|
||||
if (service->max_streams_per_circuit < 0) {
|
||||
log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
|
||||
"streams per circuit; ignoring.",
|
||||
rend_service_escaped_dir(service));
|
||||
rend_service_free(service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (service->max_streams_close_circuit < 0 ||
|
||||
service->max_streams_close_circuit > 1) {
|
||||
log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
|
||||
"max streams handling; ignoring.",
|
||||
rend_service_escaped_dir(service));
|
||||
rend_service_free(service);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (service->auth_type != REND_NO_AUTH &&
|
||||
smartlist_len(service->clients) == 0) {
|
||||
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
|
||||
@ -539,6 +563,33 @@ rend_config_services(const or_options_t *options, int validate_only)
|
||||
log_info(LD_CONFIG,
|
||||
"HiddenServiceDirGroupReadable=%d for %s",
|
||||
service->dir_group_readable, service->directory);
|
||||
} else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
|
||||
service->max_streams_per_circuit = (int)tor_parse_long(line->value,
|
||||
10, 0, 65535, &ok, NULL);
|
||||
if (!ok) {
|
||||
log_warn(LD_CONFIG,
|
||||
"HiddenServiceMaxStreams should be between 0 and %d, not %s",
|
||||
65535, line->value);
|
||||
rend_service_free(service);
|
||||
return -1;
|
||||
}
|
||||
log_info(LD_CONFIG,
|
||||
"HiddenServiceMaxStreams=%d for %s",
|
||||
service->max_streams_per_circuit, service->directory);
|
||||
} else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
|
||||
service->max_streams_close_circuit = (int)tor_parse_long(line->value,
|
||||
10, 0, 1, &ok, NULL);
|
||||
if (!ok) {
|
||||
log_warn(LD_CONFIG,
|
||||
"HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, not %s",
|
||||
line->value);
|
||||
rend_service_free(service);
|
||||
return -1;
|
||||
}
|
||||
log_info(LD_CONFIG,
|
||||
"HiddenServiceMaxStreamsCloseCircuit=%d for %s",
|
||||
(int)service->max_streams_close_circuit, service->directory);
|
||||
|
||||
} else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
|
||||
/* Parse auth type and comma-separated list of client names and add a
|
||||
* rend_authorized_client_t for each client to the service's list
|
||||
@ -758,7 +809,10 @@ rend_config_services(const or_options_t *options, int validate_only)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible.
|
||||
/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with
|
||||
* <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
|
||||
* and circuit closure on max streams being exceeded set by
|
||||
* <b>max_streams_close_circuit</b>.
|
||||
*
|
||||
* Regardless of sucess/failure, callers should not touch pk/ports after
|
||||
* calling this routine, and may assume that correct cleanup has been done
|
||||
@ -769,6 +823,8 @@ rend_config_services(const or_options_t *options, int validate_only)
|
||||
rend_service_add_ephemeral_status_t
|
||||
rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
smartlist_t *ports,
|
||||
int max_streams_per_circuit,
|
||||
int max_streams_close_circuit,
|
||||
char **service_id_out)
|
||||
{
|
||||
*service_id_out = NULL;
|
||||
@ -782,6 +838,8 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
s->ports = ports;
|
||||
s->intro_period_started = time(NULL);
|
||||
s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
|
||||
s->max_streams_per_circuit = max_streams_per_circuit;
|
||||
s->max_streams_close_circuit = max_streams_close_circuit;
|
||||
if (rend_service_derive_key_digests(s) < 0) {
|
||||
rend_service_free(s);
|
||||
return RSAE_BADPRIVKEY;
|
||||
@ -3795,6 +3853,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
|
||||
serviceid, (unsigned)circ->base_.n_circ_id);
|
||||
return -2;
|
||||
}
|
||||
if (service->max_streams_per_circuit > 0) {
|
||||
/* Enforce the streams-per-circuit limit, and refuse to provide a
|
||||
* mapping if this circuit will exceed the limit. */
|
||||
#define MAX_STREAM_WARN_INTERVAL 600
|
||||
static struct ratelim_t stream_ratelim =
|
||||
RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
|
||||
if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
|
||||
log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
|
||||
"Maximum streams per circuit limit reached on rendezvous "
|
||||
"circuit %u; %s. Circuit has %d out of %d streams.",
|
||||
(unsigned)circ->base_.n_circ_id,
|
||||
service->max_streams_close_circuit ?
|
||||
"closing circuit" :
|
||||
"ignoring open stream request",
|
||||
circ->rend_data->nr_streams,
|
||||
service->max_streams_per_circuit);
|
||||
return service->max_streams_close_circuit ? -2 : -1;
|
||||
}
|
||||
}
|
||||
matching_ports = smartlist_new();
|
||||
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
|
||||
{
|
||||
|
@ -117,6 +117,8 @@ typedef enum {
|
||||
} rend_service_add_ephemeral_status_t;
|
||||
rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
|
||||
smartlist_t *ports,
|
||||
int max_streams_per_circuit,
|
||||
int max_streams_close_circuit,
|
||||
char **service_id_out);
|
||||
int rend_service_del_ephemeral(const char *service_id);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user