Parcourir la source

Merge remote-tracking branch 'yawning/bug16052a_027'

Nick Mathewson il y a 10 ans
Parent
commit
eb7f4d0059
9 fichiers modifiés avec 141 ajouts et 10 suppressions
  1. 5 0
      changes/feature16052
  2. 10 0
      doc/tor.1.txt
  3. 19 8
      src/or/circuituse.c
  4. 2 0
      src/or/config.c
  5. 2 0
      src/or/connection_edge.c
  6. 20 1
      src/or/control.c
  7. 3 0
      src/or/or.h
  8. 78 1
      src/or/rendservice.c
  9. 2 0
      src/or/rendservice.h

+ 5 - 0
changes/feature16052

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

+ 10 - 0
doc/tor.1.txt

@@ -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
    not an authorization mechanism; it is instead meant to be a mild
    inconvenience to port-scanners.) (Default: 0)
    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**::
 [[RendPostPeriod]] **RendPostPeriod** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
     Every time the specified period elapses, Tor uploads any rendezvous
     Every time the specified period elapses, Tor uploads any rendezvous
     service descriptors to the directory servers. This information  is also
     service descriptors to the directory servers. This information  is also

+ 19 - 8
src/or/circuituse.c

@@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
 
 
   if (CIRCUIT_IS_ORIGIN(circ)) {
   if (CIRCUIT_IS_ORIGIN(circ)) {
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
     origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+    int removed = 0;
     if (conn == origin_circ->p_streams) {
     if (conn == origin_circ->p_streams) {
       origin_circ->p_streams = conn->next_stream;
       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;
       return;
     }
     }
   } else {
   } else {

+ 2 - 0
src/or/config.c

@@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
   VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
   VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
   VAR("HiddenServiceAllowUnknownPorts",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(HiddenServiceStatistics,     BOOL,     "0"),
   V(HidServAuth,                 LINELIST, NULL),
   V(HidServAuth,                 LINELIST, NULL),
   V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
   V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),

+ 2 - 0
src/or/connection_edge.c

@@ -2860,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
     origin_circ->p_streams = n_stream;
     origin_circ->p_streams = n_stream;
     assert_circuit_ok(circ);
     assert_circuit_ok(circ);
 
 
+    origin_circ->rend_data->nr_streams++;
+
     connection_exit_connect(n_stream);
     connection_exit_connect(n_stream);
 
 
     /* For path bias: This circuit was used successfully */
     /* For path bias: This circuit was used successfully */

+ 20 - 1
src/or/control.c

@@ -3566,9 +3566,12 @@ handle_control_add_onion(control_connection_t *conn,
   smartlist_t *port_cfgs = smartlist_new();
   smartlist_t *port_cfgs = smartlist_new();
   int discard_pk = 0;
   int discard_pk = 0;
   int detach = 0;
   int detach = 0;
+  int max_streams = 0;
+  int max_streams_close_circuit = 0;
   for (size_t i = 1; i < arg_len; i++) {
   for (size_t i = 1; i < arg_len; i++) {
     static const char *port_prefix = "Port=";
     static const char *port_prefix = "Port=";
     static const char *flags_prefix = "Flags=";
     static const char *flags_prefix = "Flags=";
+    static const char *max_s_prefix = "MaxStreams=";
 
 
     const char *arg = smartlist_get(args, i);
     const char *arg = smartlist_get(args, i);
     if (!strcasecmpstart(arg, port_prefix)) {
     if (!strcasecmpstart(arg, port_prefix)) {
@@ -3582,15 +3585,27 @@ handle_control_add_onion(control_connection_t *conn,
         goto out;
         goto out;
       }
       }
       smartlist_add(port_cfgs, cfg);
       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)) {
     } else if (!strcasecmpstart(arg, flags_prefix)) {
       /* "Flags=Flag[,Flag]", where Flag can be:
       /* "Flags=Flag[,Flag]", where Flag can be:
        *   * 'DiscardPK' - If tor generates the keypair, do not include it in
        *   * 'DiscardPK' - If tor generates the keypair, do not include it in
        *                   the response.
        *                   the response.
        *   * 'Detach' - Do not tie this onion service to any particular control
        *   * 'Detach' - Do not tie this onion service to any particular control
        *                connection.
        *                connection.
+       *   * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
+       *                                exceeded.
        */
        */
       static const char *discard_flag = "DiscardPK";
       static const char *discard_flag = "DiscardPK";
       static const char *detach_flag = "Detach";
       static const char *detach_flag = "Detach";
+      static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
 
 
       smartlist_t *flags = smartlist_new();
       smartlist_t *flags = smartlist_new();
       int bad = 0;
       int bad = 0;
@@ -3607,6 +3622,8 @@ handle_control_add_onion(control_connection_t *conn,
           discard_pk = 1;
           discard_pk = 1;
         } else if (!strcasecmp(flag, detach_flag)) {
         } else if (!strcasecmp(flag, detach_flag)) {
           detach = 1;
           detach = 1;
+        } else if (!strcasecmp(flag, max_s_close_flag)) {
+          max_streams_close_circuit = 1;
         } else {
         } else {
           connection_printf_to_buf(conn,
           connection_printf_to_buf(conn,
                                    "512 Invalid 'Flags' argument: %s\r\n",
                                    "512 Invalid 'Flags' argument: %s\r\n",
@@ -3652,7 +3669,9 @@ handle_control_add_onion(control_connection_t *conn,
    * regardless of success/failure.
    * regardless of success/failure.
    */
    */
   char *service_id = NULL;
   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. */
   port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
   switch (ret) {
   switch (ret) {
   case RSAE_OKAY:
   case RSAE_OKAY:

+ 3 - 0
src/or/or.h

@@ -818,6 +818,9 @@ typedef struct rend_data_t {
   /** List of HSDir fingerprints on which this request has been sent to.
   /** List of HSDir fingerprints on which this request has been sent to.
    * This contains binary identity digest of the directory. */
    * This contains binary identity digest of the directory. */
   smartlist_t *hsdirs_fp;
   smartlist_t *hsdirs_fp;
+
+  /** Number of streams associated with this rendezvous circuit. */
+  int nr_streams;
 } rend_data_t;
 } rend_data_t;
 
 
 /** Time interval for tracking replays of DH public keys received in
 /** Time interval for tracking replays of DH public keys received in

+ 78 - 1
src/or/rendservice.c

@@ -147,6 +147,13 @@ typedef struct rend_service_t {
   /** If true, we don't close circuits for making requests to unsupported
   /** If true, we don't close circuits for making requests to unsupported
    * ports. */
    * ports. */
   int allow_unknown_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;
 } rend_service_t;
 
 
 /** Returns a escaped string representation of the service, <b>s</b>.
 /** 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();
   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 &&
   if (service->auth_type != REND_NO_AUTH &&
       smartlist_len(service->clients) == 0) {
       smartlist_len(service->clients) == 0) {
     log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
     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,
         log_info(LD_CONFIG,
                  "HiddenServiceDirGroupReadable=%d for %s",
                  "HiddenServiceDirGroupReadable=%d for %s",
                  service->dir_group_readable, service->directory);
                  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")) {
     } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
       /* Parse auth type and comma-separated list of client names and add a
       /* 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
        * 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;
   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
  * Regardless of sucess/failure, callers should not touch pk/ports after
  * calling this routine, and may assume that correct cleanup has been done
  * 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_status_t
 rend_service_add_ephemeral(crypto_pk_t *pk,
 rend_service_add_ephemeral(crypto_pk_t *pk,
                            smartlist_t *ports,
                            smartlist_t *ports,
+                           int max_streams_per_circuit,
+                           int max_streams_close_circuit,
                            char **service_id_out)
                            char **service_id_out)
 {
 {
   *service_id_out = NULL;
   *service_id_out = NULL;
@@ -782,6 +838,8 @@ rend_service_add_ephemeral(crypto_pk_t *pk,
   s->ports = ports;
   s->ports = ports;
   s->intro_period_started = time(NULL);
   s->intro_period_started = time(NULL);
   s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
   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) {
   if (rend_service_derive_key_digests(s) < 0) {
     rend_service_free(s);
     rend_service_free(s);
     return RSAE_BADPRIVKEY;
     return RSAE_BADPRIVKEY;
@@ -3795,6 +3853,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
              serviceid, (unsigned)circ->base_.n_circ_id);
              serviceid, (unsigned)circ->base_.n_circ_id);
     return -2;
     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();
   matching_ports = smartlist_new();
   SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
   SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
   {
   {

+ 2 - 0
src/or/rendservice.h

@@ -117,6 +117,8 @@ typedef enum {
 } rend_service_add_ephemeral_status_t;
 } rend_service_add_ephemeral_status_t;
 rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
 rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
                                smartlist_t *ports,
                                smartlist_t *ports,
+                               int max_streams_per_circuit,
+                               int max_streams_close_circuit,
                                char **service_id_out);
                                char **service_id_out);
 int rend_service_del_ephemeral(const char *service_id);
 int rend_service_del_ephemeral(const char *service_id);