2003-04-24 Havoc Pennington <hp@redhat.com>
authorHavoc Pennington <hp@redhat.com>
Thu, 24 Apr 2003 22:30:38 +0000 (22:30 +0000)
committerHavoc Pennington <hp@redhat.com>
Thu, 24 Apr 2003 22:30:38 +0000 (22:30 +0000)
* 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.

ChangeLog
bus/bus.c
bus/bus.h
bus/config-parser.c
bus/config-parser.h
doc/config-file.txt
test/data/valid-config-files/basic.conf

index f770a53..7accabf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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) 
index 53ad8e6..7bb4bf9 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -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,
index e4e6dab..7cffb33 100644 (file)
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -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);
index bf959ae..bd1c47b 100644 (file)
@@ -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>
 
index 15644ee..acf868e 100644 (file)
@@ -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.
index 62bb413..ed3cdfa 100644 (file)
@@ -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"
index d109d71..5addd69 100644 (file)
   <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>