diff options
| author | Havoc Pennington <hp@redhat.com> | 2003-04-24 22:30:38 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2003-04-24 22:30:38 +0000 | 
| commit | 3185d7edde8ffc7672aa7d771966b0f1e0158aea (patch) | |
| tree | 03f33d4ca7737d26badd04f2fd6ee95db0ffdba7 | |
| parent | 1820f3bd0a5a4b0ab14dbcc80ba1b68d2c48e01d (diff) | |
2003-04-24  Havoc Pennington  <hp@redhat.com>
	* test/data/valid-config-files/basic.conf: add <limit> tags to
	this test
	* bus/config-parser.h, bus/config-parser.c, bus/bus.c: Implement
	<limit> tag in configuration file.
| -rw-r--r-- | ChangeLog | 8 | ||||
| -rw-r--r-- | bus/bus.c | 55 | ||||
| -rw-r--r-- | bus/bus.h | 13 | ||||
| -rw-r--r-- | bus/config-parser.c | 199 | ||||
| -rw-r--r-- | bus/config-parser.h | 2 | ||||
| -rw-r--r-- | doc/config-file.txt | 30 | ||||
| -rw-r--r-- | test/data/valid-config-files/basic.conf | 10 | 
7 files changed, 270 insertions, 47 deletions
| @@ -1,5 +1,13 @@  2003-04-24  Havoc Pennington  <hp@redhat.com> +	* test/data/valid-config-files/basic.conf: add <limit> tags to +	this test +	 +	* bus/config-parser.h, bus/config-parser.c, bus/bus.c: Implement +	<limit> tag in configuration file. +	 +2003-04-24  Havoc Pennington  <hp@redhat.com> +  	* bus/dispatch.c: somehow missed some name_is  	* dbus/dbus-timeout.c (_dbus_timeout_set_enabled)  @@ -45,14 +45,7 @@ struct BusContext    BusRegistry *registry;    BusPolicy *policy;    DBusUserDatabase *user_database; -  long max_incoming_bytes;          /**< How many incoming messages for a connection */ -  long max_outgoing_bytes;          /**< How many outgoing bytes can be queued for a connection */ -  long max_message_size;            /**< Max size of a single message in bytes */ -  int activation_timeout;           /**< How long to wait for an activation to time out */ -  int auth_timeout;                 /**< How long to wait for an authentication to time out */ -  int max_completed_connections;    /**< Max number of authorized connections */ -  int max_incomplete_connections;   /**< Max number of incomplete connections */ -  int max_connections_per_user;     /**< Max number of connections auth'd as same user */ +  BusLimits limits;  };  static int server_data_slot = -1; @@ -215,10 +208,10 @@ new_connection_callback (DBusServer     *server,      }    dbus_connection_set_max_received_size (new_connection, -                                         context->max_incoming_bytes); +                                         context->limits.max_incoming_bytes);    dbus_connection_set_max_message_size (new_connection, -                                        context->max_message_size); +                                        context->limits.max_message_size);    /* on OOM, we won't have ref'd the connection so it will die. */  } @@ -357,38 +350,14 @@ bus_context_new (const DBusString *config_file,    context->refcount = 1; +  /* get our limits and timeout lengths */ +  bus_config_parser_get_limits (parser, &context->limits); +      /* we need another ref of the server data slot for the context     * to own     */    if (!server_data_slot_ref ())      _dbus_assert_not_reached ("second ref of server data slot failed"); - -  /* Make up some numbers! woot! */ -  context->max_incoming_bytes = _DBUS_ONE_MEGABYTE * 63;   -  context->max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 63; -  context->max_message_size = _DBUS_ONE_MEGABYTE * 32; -   -#ifdef DBUS_BUILD_TESTS -  context->activation_timeout = 6000;  /* 6 seconds */ -#else -  context->activation_timeout = 15000; /* 15 seconds */ -#endif - -  /* Making this long risks making a DOS attack easier, but too short -   * and legitimate auth will fail.  If interactive auth (ask user for -   * password) is allowed, then potentially it has to be quite long. -   * Ultimately it needs to come from the configuration file. -   */      -  context->auth_timeout = 3000; /* 3 seconds */ - -  context->max_incomplete_connections = 32; -  context->max_connections_per_user = 128; - -  /* Note that max_completed_connections / max_connections_per_user -   * is the number of users that would have to work together to -   * DOS all the other users. -   */ -  context->max_completed_connections = 1024;    context->user_database = _dbus_user_database_new ();    if (context->user_database == NULL) @@ -829,31 +798,31 @@ int  bus_context_get_activation_timeout (BusContext *context)  { -  return context->activation_timeout; +  return context->limits.activation_timeout;  }  int  bus_context_get_auth_timeout (BusContext *context)  { -  return context->auth_timeout; +  return context->limits.auth_timeout;  }  int  bus_context_get_max_completed_connections (BusContext *context)  { -  return context->max_completed_connections; +  return context->limits.max_completed_connections;  }  int  bus_context_get_max_incomplete_connections (BusContext *context)  { -  return context->max_incomplete_connections; +  return context->limits.max_incomplete_connections;  }  int  bus_context_get_max_connections_per_user (BusContext *context)  { -  return context->max_connections_per_user; +  return context->limits.max_connections_per_user;  }  dbus_bool_t @@ -919,7 +888,7 @@ bus_context_check_security_policy (BusContext     *context,    /* See if limits on size have been exceeded */    if (recipient &&        dbus_connection_get_outgoing_size (recipient) > -      context->max_outgoing_bytes) +      context->limits.max_outgoing_bytes)      {        const char *dest = dbus_message_get_destination (message);        dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, @@ -41,6 +41,19 @@ typedef struct BusRegistry      BusRegistry;  typedef struct BusService       BusService;  typedef struct BusTransaction   BusTransaction; + +typedef struct +{ +  long max_incoming_bytes;          /**< How many incoming messages for a connection */ +  long max_outgoing_bytes;          /**< How many outgoing bytes can be queued for a connection */ +  long max_message_size;            /**< Max size of a single message in bytes */ +  int activation_timeout;           /**< How long to wait for an activation to time out */ +  int auth_timeout;                 /**< How long to wait for an authentication to time out */ +  int max_completed_connections;    /**< Max number of authorized connections */ +  int max_incomplete_connections;   /**< Max number of incomplete connections */ +  int max_connections_per_user;     /**< Max number of connections auth'd as same user */ +} BusLimits; +  BusContext*       bus_context_new                            (const DBusString *config_file,                                                                int               print_addr_fd,                                                                DBusError        *error); diff --git a/bus/config-parser.c b/bus/config-parser.c index bf959ae2..bd1c47b8 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -78,6 +78,12 @@ typedef struct        unsigned long gid_or_uid;            } policy; +    struct +    { +      char *name; +      long value; +    } limit; +        } d;  } Element; @@ -101,6 +107,8 @@ struct BusConfigParser    DBusList *service_dirs; /**< Directories to look for services in */    BusPolicy *policy;     /**< Security policy */ + +  BusLimits limits;      /**< Limits */    unsigned int fork : 1; /**< TRUE to fork into daemon mode */ @@ -175,7 +183,9 @@ push_element (BusConfigParser *parser,  static void  element_free (Element *e)  { - +  if (e->type == ELEMENT_LIMIT) +    dbus_free (e->d.limit.name); +      dbus_free (e);  } @@ -280,6 +290,32 @@ bus_config_parser_new (const DBusString *basedir)        dbus_free (parser);        return NULL;      } + +  /* Make up some numbers! woot! */ +  parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 63; +  parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 63; +  parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32; +   +#ifdef DBUS_BUILD_TESTS +  parser->limits.activation_timeout = 6000;  /* 6 seconds */ +#else +  parser->limits.activation_timeout = 15000; /* 15 seconds */ +#endif + +  /* Making this long risks making a DOS attack easier, but too short +   * and legitimate auth will fail.  If interactive auth (ask user for +   * password) is allowed, then potentially it has to be quite long. +   */      +  parser->limits.auth_timeout = 3000; /* 3 seconds */ + +  parser->limits.max_incomplete_connections = 32; +  parser->limits.max_connections_per_user = 128; + +  /* Note that max_completed_connections / max_connections_per_user +   * is the number of users that would have to work together to +   * DOS all the other users. +   */ +  parser->limits.max_completed_connections = 1024;    parser->refcount = 1; @@ -713,6 +749,41 @@ start_busconfig_child (BusConfigParser   *parser,        return TRUE;      } +  else if (strcmp (element_name, "limit") == 0) +    { +      Element *e; +      const char *name; + +      if ((e = push_element (parser, ELEMENT_LIMIT)) == NULL) +        { +          BUS_SET_OOM (error); +          return FALSE; +        } +       +      if (!locate_attributes (parser, "limit", +                              attribute_names, +                              attribute_values, +                              error, +                              "name", &name, +                              NULL)) +        return FALSE; + +      if (name == NULL) +        { +          dbus_set_error (error, DBUS_ERROR_FAILED, +                          "<limit> element must have a \"name\" attribute"); +          return FALSE; +        } + +      e->d.limit.name = _dbus_strdup (name); +      if (e->d.limit.name == NULL) +        { +          BUS_SET_OOM (error); +          return FALSE; +        } + +      return TRUE; +    }    else      {        dbus_set_error (error, DBUS_ERROR_FAILED, @@ -1087,6 +1158,91 @@ bus_config_parser_start_element (BusConfigParser   *parser,      }    } +static dbus_bool_t +set_limit (BusConfigParser *parser, +           const char      *name, +           long             value, +           DBusError       *error) +{ +  dbus_bool_t must_be_positive; +  dbus_bool_t must_be_int; + +  must_be_int = FALSE; +  must_be_positive = FALSE; +   +  if (strcmp (name, "max_incoming_bytes") == 0) +    { +      must_be_positive = TRUE; +      parser->limits.max_incoming_bytes = value; +    } +  else if (strcmp (name, "max_outgoing_bytes") == 0) +    { +      must_be_positive = TRUE; +      parser->limits.max_outgoing_bytes = value; +    } +  else if (strcmp (name, "max_message_size") == 0) +    { +      must_be_positive = TRUE; +      parser->limits.max_message_size = value; +    } +  else if (strcmp (name, "activation_timeout") == 0) +    { +      must_be_positive = TRUE; +      must_be_int = TRUE; +      parser->limits.activation_timeout = value; +    } +  else if (strcmp (name, "auth_timeout") == 0) +    { +      must_be_positive = TRUE; +      must_be_int = TRUE; +      parser->limits.auth_timeout = value; +    } +  else if (strcmp (name, "max_completed_connections") == 0) +    { +      must_be_positive = TRUE; +      must_be_int = TRUE; +      parser->limits.max_completed_connections = value; +    } +  else if (strcmp (name, "max_incomplete_connections") == 0) +    { +      must_be_positive = TRUE; +      must_be_int = TRUE; +      parser->limits.max_incomplete_connections = value; +    } +  else if (strcmp (name, "max_connections_per_user") == 0) +    { +      must_be_positive = TRUE; +      must_be_int = TRUE; +      parser->limits.max_connections_per_user = value; +    } +  else +    { +      dbus_set_error (error, DBUS_ERROR_FAILED, +                      "There is no limit called \"%s\"\n", +                      name); +      return FALSE; +    } +   +  if (must_be_positive && value < 0) +    { +      dbus_set_error (error, DBUS_ERROR_FAILED, +                      "<limit name=\"%s\"> must be a positive number\n", +                      name); +      return FALSE; +    } + +  if (must_be_int && +      (value < _DBUS_INT_MIN || value > _DBUS_INT_MAX)) +    { +      dbus_set_error (error, DBUS_ERROR_FAILED, +                      "<limit name=\"%s\"> value is too large\n", +                      name); +      return FALSE; +    } + +  return TRUE;   +} +  dbus_bool_t  bus_config_parser_end_element (BusConfigParser   *parser,                                 const char        *element_name, @@ -1142,6 +1298,7 @@ bus_config_parser_end_element (BusConfigParser   *parser,      case ELEMENT_AUTH:      case ELEMENT_SERVICEDIR:      case ELEMENT_INCLUDEDIR: +    case ELEMENT_LIMIT:        if (!e->had_content)          {            dbus_set_error (error, DBUS_ERROR_FAILED, @@ -1149,11 +1306,17 @@ bus_config_parser_end_element (BusConfigParser   *parser,                            element_type_to_name (e->type));            return FALSE;          } + +      if (e->type == ELEMENT_LIMIT) +        { +          if (!set_limit (parser, e->d.limit.name, e->d.limit.value, +                          error)) +            return FALSE; +        }        break;      case ELEMENT_BUSCONFIG:      case ELEMENT_POLICY: -    case ELEMENT_LIMIT:      case ELEMENT_ALLOW:      case ELEMENT_DENY:      case ELEMENT_FORK: @@ -1359,7 +1522,6 @@ bus_config_parser_content (BusConfigParser   *parser,      case ELEMENT_BUSCONFIG:      case ELEMENT_POLICY: -    case ELEMENT_LIMIT:      case ELEMENT_ALLOW:      case ELEMENT_DENY:      case ELEMENT_FORK: @@ -1534,6 +1696,29 @@ bus_config_parser_content (BusConfigParser   *parser,          _dbus_string_free (&full_path);        }        break; + +    case ELEMENT_LIMIT: +      { +        long val; + +        e->had_content = TRUE; + +        val = 0; +        if (!_dbus_string_parse_int (content, 0, &val, NULL)) +          { +            dbus_set_error (error, DBUS_ERROR_FAILED, +                            "<limit name=\"%s\"> element has invalid value (could not parse as integer)", +                            e->d.limit.name); +            return FALSE; +          } + +        e->d.limit.value = val; + +        _dbus_verbose ("Loaded value %ld for limit %s\n", +                       e->d.limit.value, +                       e->d.limit.name); +      } +      break;      }    _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -1625,6 +1810,14 @@ bus_config_parser_steal_policy (BusConfigParser *parser)    return policy;  } +/* Overwrite any limits that were set in the configuration file */ +void +bus_config_parser_get_limits (BusConfigParser *parser, +                              BusLimits       *limits) +{ +  *limits = parser->limits; +} +  #ifdef DBUS_BUILD_TESTS  #include <stdio.h> diff --git a/bus/config-parser.h b/bus/config-parser.h index 15644ee6..acf868ef 100644 --- a/bus/config-parser.h +++ b/bus/config-parser.h @@ -64,6 +64,8 @@ dbus_bool_t bus_config_parser_get_fork         (BusConfigParser *parser);  const char* bus_config_parser_get_pidfile      (BusConfigParser *parser);  DBusList**  bus_config_parser_get_service_dirs (BusConfigParser *parser);  BusPolicy*  bus_config_parser_steal_policy     (BusConfigParser *parser); +void        bus_config_parser_get_limits       (BusConfigParser *parser, +                                                BusLimits       *limits);  /* Loader functions (backended off one of the XML parsers).  Returns a   * finished ConfigParser. diff --git a/doc/config-file.txt b/doc/config-file.txt index 62bb4137..ed3cdfab 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -131,7 +131,35 @@ Elements:      Appears below a <policy> element and establishes a resource      limit. For example:        <limit name="max_message_size">64</limit> -      <limit name="max_connections">512</limit> +      <limit name="max_completed_connections">512</limit> + +    Available limits are: +      "max_incoming_bytes"         : total size in bytes of messages +                                     incoming from a connection +      "max_outgoing_bytes"         : total size in bytes of messages +                                     queued up for a connection +      "max_message_size"           : max size of a single message in +                                     bytes +      "activation_timeout"         : milliseconds (thousandths) until  +                                     an activated service has to connect +      "auth_timeout"               : milliseconds (thousandths) a +                                     connection is given to +                                     authenticate +      "max_completed_connections"  : max number of authenticated connections   +      "max_incomplete_connections" : max number of unauthenticated +                                     connections +      "max_connections_per_user"   : max number of completed connections from +                                     the same user + +    Some notes: + +       - the max incoming/outgoing queue sizes allow a new message  +         to be queued if one byte remains below the max. So you can  +         in fact exceed the max by max_message_size + +       - max_completed_connections / max_connections_per_user is  +         the number of users that can work together to DOS all  +         other users by using up all connections   <deny>    send="messagename" diff --git a/test/data/valid-config-files/basic.conf b/test/data/valid-config-files/basic.conf index d109d71d..5addd692 100644 --- a/test/data/valid-config-files/basic.conf +++ b/test/data/valid-config-files/basic.conf @@ -10,4 +10,14 @@    <policy context="default">      <allow user="*"/>    </policy> + +  <limit name="max_incoming_bytes">5000</limit>    +  <limit name="max_outgoing_bytes">5000</limit> +  <limit name="max_message_size">300</limit> +  <limit name="activation_timeout">5000</limit> +  <limit name="auth_timeout">6000</limit> +  <limit name="max_completed_connections">50</limit>   +  <limit name="max_incomplete_connections">80</limit> +  <limit name="max_connections_per_user">64</limit> +                                     </busconfig> | 
