Merge remote-tracking branch 'yawning/bug16052a_027'

This commit is contained in:
Nick Mathewson 2015-05-21 10:48:52 -04:00
commit eb7f4d0059
9 changed files with 141 additions and 10 deletions

5
changes/feature16052 Normal file
View 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.

View File

@ -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

View File

@ -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 {

View File

@ -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"),

View File

@ -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 */

View File

@ -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:

View File

@ -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

View File

@ -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,
{

View File

@ -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);