ELEMENT_TYPE
} ElementType;
+typedef enum
+{
+ /* we ignore policies for unknown groups/users */
+ POLICY_IGNORED,
+
+ /* non-ignored */
+ POLICY_DEFAULT,
+ POLICY_MANDATORY,
+ POLICY_USER,
+ POLICY_GROUP
+} PolicyType;
+
typedef struct
{
ElementType type;
struct
{
- char *context;
- char *user;
- char *group;
- DBusList *rules;
+ PolicyType type;
+ unsigned long gid_or_uid;
} policy;
struct
{
- int foo;
+ char *name;
+ long value;
} limit;
-
+
} d;
} Element;
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 */
static void
element_free (Element *e)
{
-
+ if (e->type == ELEMENT_LIMIT)
+ dbus_free (e->d.limit.name);
+
dbus_free (e);
}
if (((parser->policy = bus_policy_new ()) == NULL) ||
!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
{
+ if (parser->policy)
+ bus_policy_unref (parser->policy);
+
_dbus_string_free (&parser->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 = 30000; /* 30 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->limits.max_pending_activations = 256;
+ parser->limits.max_services_per_connection = 256;
parser->refcount = 1;
return FALSE;
}
+ e->d.policy.type = POLICY_IGNORED;
+
if (!locate_attributes (parser, "policy",
attribute_names,
attribute_values,
{
if (strcmp (context, "default") == 0)
{
-
+ e->d.policy.type = POLICY_DEFAULT;
}
else if (strcmp (context, "mandatory") == 0)
{
-
+ e->d.policy.type = POLICY_MANDATORY;
}
else
{
context);
return FALSE;
}
-
- /* FIXME */
-
}
else if (user != NULL)
{
- /* FIXME */
+ DBusString username;
+ _dbus_string_init_const (&username, user);
+ if (_dbus_get_user_id (&username,
+ &e->d.policy.gid_or_uid))
+ e->d.policy.type = POLICY_USER;
+ else
+ _dbus_warn ("Unknown username \"%s\" in message bus configuration file\n",
+ user);
}
else if (group != NULL)
{
- /* FIXME */
+ DBusString group_name;
+ _dbus_string_init_const (&group_name, group);
+ if (_dbus_get_group_id (&group_name,
+ &e->d.policy.gid_or_uid))
+ e->d.policy.type = POLICY_GROUP;
+ else
+ _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
+ group);
}
else
{
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,
}
static dbus_bool_t
+append_rule_from_element (BusConfigParser *parser,
+ const char *element_name,
+ const char **attribute_names,
+ const char **attribute_values,
+ dbus_bool_t allow,
+ DBusError *error)
+{
+ const char *send;
+ const char *receive;
+ const char *own;
+ const char *send_to;
+ const char *receive_from;
+ const char *user;
+ const char *group;
+ BusPolicyRule *rule;
+
+ if (!locate_attributes (parser, element_name,
+ attribute_names,
+ attribute_values,
+ error,
+ "send", &send,
+ "receive", &receive,
+ "own", &own,
+ "send_to", &send_to,
+ "receive_from", &receive_from,
+ "user", &user,
+ "group", &group,
+ NULL))
+ return FALSE;
+
+ if (!(send || receive || own || send_to || receive_from ||
+ user || group))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Element <%s> must have one or more attributes",
+ element_name);
+ return FALSE;
+ }
+
+ if (((send && own) ||
+ (send && receive) ||
+ (send && receive_from) ||
+ (send && user) ||
+ (send && group)) ||
+
+ ((receive && own) ||
+ (receive && send_to) ||
+ (receive && user) ||
+ (receive && group)) ||
+
+ ((own && send_to) ||
+ (own && receive_from) ||
+ (own && user) ||
+ (own && group)) ||
+
+ ((send_to && receive_from) ||
+ (send_to && user) ||
+ (send_to && group)) ||
+
+ ((receive_from && user) ||
+ (receive_from && group)) ||
+
+ (user && group))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Invalid combination of attributes on element <%s>, "
+ "only send/send_to or receive/receive_from may be paired",
+ element_name);
+ return FALSE;
+ }
+
+ rule = NULL;
+
+ /* In BusPolicyRule, NULL represents wildcard.
+ * In the config file, '*' represents it.
+ */
+#define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0')
+
+ if (send || send_to)
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (IS_WILDCARD (send))
+ send = NULL;
+ if (IS_WILDCARD (send_to))
+ send_to = NULL;
+
+ rule->d.send.message_name = _dbus_strdup (send);
+ rule->d.send.destination = _dbus_strdup (send_to);
+ if (send && rule->d.send.message_name == NULL)
+ goto nomem;
+ if (send_to && rule->d.send.destination == NULL)
+ goto nomem;
+ }
+ else if (receive || receive_from)
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (IS_WILDCARD (receive))
+ receive = NULL;
+
+ if (IS_WILDCARD (receive_from))
+ receive_from = NULL;
+
+ rule->d.receive.message_name = _dbus_strdup (receive);
+ rule->d.receive.origin = _dbus_strdup (receive_from);
+ if (receive && rule->d.receive.message_name == NULL)
+ goto nomem;
+ if (receive_from && rule->d.receive.origin == NULL)
+ goto nomem;
+ }
+ else if (own)
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ if (IS_WILDCARD (own))
+ own = NULL;
+
+ rule->d.own.service_name = _dbus_strdup (own);
+ if (own && rule->d.own.service_name == NULL)
+ goto nomem;
+ }
+ else if (user)
+ {
+ if (IS_WILDCARD (user))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.user.uid = DBUS_UID_UNSET;
+ }
+ else
+ {
+ DBusString username;
+ dbus_uid_t uid;
+
+ _dbus_string_init_const (&username, user);
+
+ if (_dbus_get_user_id (&username, &uid))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.user.uid = uid;
+ }
+ else
+ {
+ _dbus_warn ("Unknown username \"%s\" on element <%s>\n",
+ user, element_name);
+ }
+ }
+ }
+ else if (group)
+ {
+ if (IS_WILDCARD (group))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.group.gid = DBUS_GID_UNSET;
+ }
+ else
+ {
+ DBusString groupname;
+ dbus_gid_t gid;
+
+ _dbus_string_init_const (&groupname, group);
+
+ if (_dbus_get_user_id (&groupname, &gid))
+ {
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ if (rule == NULL)
+ goto nomem;
+
+ rule->d.group.gid = gid;
+ }
+ else
+ {
+ _dbus_warn ("Unknown group \"%s\" on element <%s>\n",
+ group, element_name);
+ }
+ }
+ }
+ else
+ _dbus_assert_not_reached ("Did not handle some combination of attributes on <allow> or <deny>");
+
+ if (rule != NULL)
+ {
+ Element *pe;
+
+ pe = peek_element (parser);
+ _dbus_assert (pe != NULL);
+ _dbus_assert (pe->type == ELEMENT_POLICY);
+
+ switch (pe->d.policy.type)
+ {
+ case POLICY_IGNORED:
+ /* drop the rule on the floor */
+ break;
+
+ case POLICY_DEFAULT:
+ if (!bus_policy_append_default_rule (parser->policy, rule))
+ goto nomem;
+ break;
+ case POLICY_MANDATORY:
+ if (!bus_policy_append_mandatory_rule (parser->policy, rule))
+ goto nomem;
+ break;
+ case POLICY_USER:
+ if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<%s> rule cannot be per-user because it has bus-global semantics",
+ element_name);
+ goto failed;
+ }
+
+ if (!bus_policy_append_user_rule (parser->policy, pe->d.policy.gid_or_uid,
+ rule))
+ goto nomem;
+ break;
+ case POLICY_GROUP:
+ if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "<%s> rule cannot be per-group because it has bus-global semantics",
+ element_name);
+ goto failed;
+ }
+
+ if (!bus_policy_append_group_rule (parser->policy, pe->d.policy.gid_or_uid,
+ rule))
+ goto nomem;
+ break;
+ }
+
+ bus_policy_rule_unref (rule);
+ rule = NULL;
+ }
+
+ return TRUE;
+
+ nomem:
+ BUS_SET_OOM (error);
+ failed:
+ if (rule)
+ bus_policy_rule_unref (rule);
+ return FALSE;
+}
+
+static dbus_bool_t
start_policy_child (BusConfigParser *parser,
const char *element_name,
const char **attribute_names,
{
if (strcmp (element_name, "allow") == 0)
{
+ if (!append_rule_from_element (parser, element_name,
+ attribute_names, attribute_values,
+ TRUE, error))
+ return FALSE;
+
if (push_element (parser, ELEMENT_ALLOW) == NULL)
{
BUS_SET_OOM (error);
}
else if (strcmp (element_name, "deny") == 0)
{
+ if (!append_rule_from_element (parser, element_name,
+ attribute_names, attribute_values,
+ FALSE, error))
+ return FALSE;
+
if (push_element (parser, ELEMENT_DENY) == NULL)
{
BUS_SET_OOM (error);
}
}
+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 if (strcmp (name, "max_pending_activations") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_pending_activations = value;
+ }
+ else if (strcmp (name, "max_services_per_connection") == 0)
+ {
+ must_be_positive = TRUE;
+ must_be_int = TRUE;
+ parser->limits.max_services_per_connection = 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,
case ELEMENT_AUTH:
case ELEMENT_SERVICEDIR:
case ELEMENT_INCLUDEDIR:
+ case ELEMENT_LIMIT:
if (!e->had_content)
{
dbus_set_error (error, DBUS_ERROR_FAILED,
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:
case ELEMENT_BUSCONFIG:
case ELEMENT_POLICY:
- case ELEMENT_LIMIT:
case ELEMENT_ALLOW:
case ELEMENT_DENY:
case ELEMENT_FORK:
_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);
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>