Commit 3185d7ed authored by Havoc Pennington's avatar Havoc Pennington

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.
parent 1820f3bd
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
......
......@@ -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);
......
......@@ -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;
......@@ -711,6 +747,41 @@ start_busconfig_child (BusConfigParser *parser,
_dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
}
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
......@@ -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>
......
......@@ -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.
......
......@@ -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"
......
......@@ -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>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment