{
return ELEMENT_DENY;
}
+ else if (strcmp (name, "check") == 0)
+ {
+ return ELEMENT_CHECK;
+ }
else if (strcmp (name, "servicehelper") == 0)
{
return ELEMENT_SERVICEHELPER;
return "allow";
case ELEMENT_DENY:
return "deny";
+ case ELEMENT_CHECK:
+ return "check";
case ELEMENT_FORK:
return "fork";
case ELEMENT_PIDFILE:
ELEMENT_LIMIT,
ELEMENT_ALLOW,
ELEMENT_DENY,
+ ELEMENT_CHECK,
ELEMENT_FORK,
ELEMENT_PIDFILE,
ELEMENT_SERVICEDIR,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
- dbus_bool_t allow,
+ BusPolicyRuleAccess access,
DBusError *error)
{
const char *log;
const char *own_prefix;
const char *user;
const char *group;
+ const char *privilege;
BusPolicyRule *rule;
"user", &user,
"group", &group,
"log", &log,
+ "privilege", &privilege,
NULL))
return FALSE;
receive_interface || receive_member || receive_error || receive_sender ||
receive_type || receive_path || eavesdrop ||
send_requested_reply || receive_requested_reply ||
+ privilege ||
own || own_prefix || user || group))
{
dbus_set_error (error, DBUS_ERROR_FAILED,
element_name);
return FALSE;
}
-
+
+ if (access == BUS_POLICY_RULE_ACCESS_CHECK)
+ {
+ if (privilege == NULL || !*privilege)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "On element <%s>, you must specify the privilege to be checked.",
+ element_name);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (privilege != NULL && *privilege)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "On element <%s>, privilege %s is used outside of a check rule.",
+ element_name, privilege);
+ return FALSE;
+ }
+ else
+ privilege = NULL; /* replace (potentially) empty string with NULL pointer, it wouldn't be used anyway */
+ }
+
/* Allowed combinations of elements are:
*
* base, must be all send or all receive:
return FALSE;
}
- rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, access);
if (rule == NULL)
goto nomem;
return FALSE;
}
- rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, access);
if (rule == NULL)
goto nomem;
}
else if (own || own_prefix)
{
- rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, access);
if (rule == NULL)
goto nomem;
{
if (IS_WILDCARD (user))
{
- rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, access);
if (rule == NULL)
goto nomem;
if (_dbus_parse_unix_user_from_config (&username, &uid))
{
- rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, access);
if (rule == NULL)
goto nomem;
{
if (IS_WILDCARD (group))
{
- rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, access);
if (rule == NULL)
goto nomem;
if (_dbus_parse_unix_group_from_config (&groupname, &gid))
{
- rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow);
+ rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, access);
if (rule == NULL)
goto nomem;
_dbus_assert (pe != NULL);
_dbus_assert (pe->type == ELEMENT_POLICY);
+ rule->privilege = _dbus_strdup (privilege);
+ if (privilege && !rule->privilege)
+ goto nomem;
+
switch (pe->d.policy.type)
{
case POLICY_IGNORED:
{
if (!append_rule_from_element (parser, element_name,
attribute_names, attribute_values,
- TRUE, error))
+ BUS_POLICY_RULE_ACCESS_ALLOW, error))
return FALSE;
if (push_element (parser, ELEMENT_ALLOW) == NULL)
{
if (!append_rule_from_element (parser, element_name,
attribute_names, attribute_values,
- FALSE, error))
+ BUS_POLICY_RULE_ACCESS_DENY, error))
return FALSE;
if (push_element (parser, ELEMENT_DENY) == NULL)
return FALSE;
}
+ return TRUE;
+ }
+ else if (strcmp (element_name, "check") == 0)
+ {
+ if (!append_rule_from_element (parser, element_name,
+ attribute_names, attribute_values,
+ BUS_POLICY_RULE_ACCESS_CHECK, error))
+ return FALSE;
+
+ if (push_element (parser, ELEMENT_CHECK) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
return TRUE;
}
else
case ELEMENT_POLICY:
case ELEMENT_ALLOW:
case ELEMENT_DENY:
+ case ELEMENT_CHECK:
case ELEMENT_FORK:
case ELEMENT_SYSLOG:
case ELEMENT_KEEP_UMASK:
case ELEMENT_POLICY:
case ELEMENT_ALLOW:
case ELEMENT_DENY:
+ case ELEMENT_CHECK:
case ELEMENT_FORK:
case ELEMENT_SYSLOG:
case ELEMENT_KEEP_UMASK:
dbus_error_init (&error);
parser = bus_config_load (full_path, TRUE, NULL, &error);
+ if (dbus_error_is_set (&error))
+ _dbus_verbose ("Failed to load file: %s\n", error.message);
if (parser == NULL)
{
_DBUS_ASSERT_ERROR_IS_SET (&error);
BusPolicyRule*
bus_policy_rule_new (BusPolicyRuleType type,
- dbus_bool_t allow)
+ BusPolicyRuleAccess access)
{
BusPolicyRule *rule;
rule->type = type;
rule->refcount = 1;
- rule->allow = allow;
+ rule->access = access;
switch (rule->type)
{
break;
case BUS_POLICY_RULE_SEND:
rule->d.send.message_type = DBUS_MESSAGE_TYPE_INVALID;
-
/* allow rules default to TRUE (only requested replies allowed)
+ * check rules default to TRUE (only requested replies are checked)
* deny rules default to FALSE (only unrequested replies denied)
*/
- rule->d.send.requested_reply = rule->allow;
+ rule->d.send.requested_reply = rule->access != BUS_POLICY_RULE_ACCESS_DENY;
break;
case BUS_POLICY_RULE_RECEIVE:
rule->d.receive.message_type = DBUS_MESSAGE_TYPE_INVALID;
/* allow rules default to TRUE (only requested replies allowed)
+ * check rules default to TRUE (only requested replies are checked)
* deny rules default to FALSE (only unrequested replies denied)
*/
- rule->d.receive.requested_reply = rule->allow;
+ rule->d.receive.requested_reply = rule->access != BUS_POLICY_RULE_ACCESS_DENY;
break;
case BUS_POLICY_RULE_OWN:
break;
case BUS_POLICY_RULE_GROUP:
break;
}
-
+
+ dbus_free (rule->privilege);
dbus_free (rule);
}
}
else
continue;
- allowed = rule->allow;
+ /* We don't intend to support <check user="..." /> and <check group="..." />
+ rules. They are treated like deny.
+ */
+ allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
}
return allowed;
/* If it's a reply, the requested_reply flag kicks in */
if (dbus_message_get_reply_serial (message) != 0)
{
- /* for allow, requested_reply=true means the rule applies
- * only when reply was requested. requested_reply=false means
- * always allow.
+ /* for allow or check requested_reply=true means the rule applies
+ * only when reply was requested. requested_reply=false means the
+ * rule always applies
*/
- if (!requested_reply && rule->allow && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
+ if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
{
- _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
+ _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n",
+ rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
continue;
}
* when the reply was not requested. requested_reply=true means the
* rule always applies.
*/
- if (requested_reply && !rule->allow && !rule->d.send.requested_reply)
+ if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.send.requested_reply)
{
_dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
continue;
/* The interface is optional in messages. For allow rules, if the message
* has no interface we want to skip the rule (and thus not allow);
* for deny rules, if the message has no interface we want to use the
- * rule (and thus deny).
+ * rule (and thus deny). Check rules are meant to be used like allow
+ * rules (they can grant access, but not remove it), so we treat it like
+ * allow here.
*/
dbus_bool_t no_interface;
no_interface = dbus_message_get_interface (message) == NULL;
- if ((no_interface && rule->allow) ||
+ if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
(!no_interface &&
strcmp (dbus_message_get_interface (message),
rule->d.send.interface) != 0))
}
/* Use this rule */
- allowed = rule->allow;
+ allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
*log = rule->d.send.log;
(*toggles)++;
}
}
- /* for allow, eavesdrop=false means the rule doesn't apply when
- * eavesdropping. eavesdrop=true means always allow.
+
+ /* for allow or check, eavesdrop=false means the rule doesn't apply when
+ * eavesdropping. eavesdrop=true means the rule always applies
*/
- if (eavesdropping && rule->allow && !rule->d.receive.eavesdrop)
+ if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop)
{
- _dbus_verbose (" (policy) skipping allow rule since it doesn't apply to eavesdropping\n");
+ _dbus_verbose (" (policy) skipping %s rule since it doesn't apply to eavesdropping\n",
+ rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
continue;
}
/* for deny, eavesdrop=true means the rule applies only when
* eavesdropping; eavesdrop=false means always deny.
*/
- if (!eavesdropping && !rule->allow && rule->d.receive.eavesdrop)
+ if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop)
{
_dbus_verbose (" (policy) skipping deny rule since it only applies to eavesdropping\n");
continue;
/* If it's a reply, the requested_reply flag kicks in */
if (dbus_message_get_reply_serial (message) != 0)
{
- /* for allow, requested_reply=true means the rule applies
- * only when reply was requested. requested_reply=false means
- * always allow.
+ /* for allow or check requested_reply=true means the rule applies
+ * only when reply was requested. requested_reply=false means the
+ * rule always applies
*/
- if (!requested_reply && rule->allow && rule->d.receive.requested_reply && !rule->d.receive.eavesdrop)
+ if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
{
- _dbus_verbose (" (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
+ _dbus_verbose (" (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n",
+ rule->access == BUS_POLICY_RULE_ACCESS_DENY ? "allow" : "deny");
continue;
}
* when the reply was not requested. requested_reply=true means the
* rule always applies.
*/
- if (requested_reply && !rule->allow && !rule->d.receive.requested_reply)
+ if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.requested_reply)
{
_dbus_verbose (" (policy) skipping deny rule since it only applies to unrequested replies\n");
continue;
/* The interface is optional in messages. For allow rules, if the message
* has no interface we want to skip the rule (and thus not allow);
* for deny rules, if the message has no interface we want to use the
- * rule (and thus deny).
+ * rule (and thus deny). Check rules are treated like allow rules.
*/
dbus_bool_t no_interface;
no_interface = dbus_message_get_interface (message) == NULL;
- if ((no_interface && rule->allow) ||
+ if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
(!no_interface &&
strcmp (dbus_message_get_interface (message),
rule->d.receive.interface) != 0))
}
/* Use this rule */
- allowed = rule->allow;
+ allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
(*toggles)++;
_dbus_verbose (" (policy) used rule, allow now = %d\n",
}
/* Use this rule */
- allowed = rule->allow;
+ allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
}
return allowed;
BUS_POLICY_RULE_GROUP
} BusPolicyRuleType;
+typedef enum
+{
+ BUS_POLICY_RULE_ACCESS_DENY,
+ BUS_POLICY_RULE_ACCESS_ALLOW,
+ /** runtime check resulting in allow or deny */
+ BUS_POLICY_RULE_ACCESS_CHECK
+} BusPolicyRuleAccess;
+
/** determines whether the rule affects a connection, or some global item */
#define BUS_POLICY_RULE_IS_PER_CLIENT(rule) (!((rule)->type == BUS_POLICY_RULE_USER || \
(rule)->type == BUS_POLICY_RULE_GROUP))
BusPolicyRuleType type;
- unsigned int allow : 1; /**< #TRUE if this allows, #FALSE if it denies */
-
+ unsigned int access : 2; /**< BusPolicyRuleAccess */
+ char *privilege; /**< for BUS_POLICY_RULE_ACCESS_CHECK */
+
union
{
struct
};
BusPolicyRule* bus_policy_rule_new (BusPolicyRuleType type,
- dbus_bool_t allow);
+ BusPolicyRuleAccess access);
BusPolicyRule* bus_policy_rule_ref (BusPolicyRule *rule);
void bus_policy_rule_unref (BusPolicyRule *rule);
doc/dbus-uuidgen.1.xml
dbus-1.pc
dbus-1-uninstalled.pc
+test/data/valid-config-files/debug-allow-all.conf
+test/data/valid-config-files/debug-allow-all-sha1.conf
+test/data/valid-config-files/debug-check-some.conf
+test/data/valid-config-files/incoming-limit.conf
+test/data/valid-config-files-system/debug-allow-all-pass.conf
+test/data/valid-config-files-system/debug-allow-all-fail.conf
+test/data/valid-service-files/org.freedesktop.DBus.TestSuite.PrivServer.service
+test/data/valid-service-files/org.freedesktop.DBus.TestSuiteEchoService.service
+test/data/valid-service-files/org.freedesktop.DBus.TestSuiteForkingEchoService.service
+test/data/valid-service-files/org.freedesktop.DBus.TestSuiteSegfaultService.service
+test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service
+test/data/valid-service-files/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service
+test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteEchoService.service
+test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteSegfaultService.service
+test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceSuccess.service
+test/data/valid-service-files-system/org.freedesktop.DBus.TestSuiteShellEchoServiceFail.service
+test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoExec.service
+test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoUser.service
+test/data/invalid-service-files-system/org.freedesktop.DBus.TestSuiteNoService.service
])
AC_OUTPUT
data/valid-config-files/debug-allow-all.conf.in \
data/valid-config-files/finite-timeout.conf.in \
data/valid-config-files/forbidding.conf.in \
+ data/valid-config-files/debug-check-some.conf.in \
data/valid-config-files/incoming-limit.conf.in \
data/valid-config-files/multi-user.conf.in \
data/valid-config-files/systemd-activation.conf.in \
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <user>mybususer</user>
+ <listen>unix:path=/foo/bar</listen>
+ <policy context="default">
+ <allow privilege="foo" send_destination="*"/> <!-- extra privilege="foo" -->
+ </policy>
+</busconfig>
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <user>mybususer</user>
+ <listen>unix:path=/foo/bar</listen>
+ <policy context="default">
+ <check send_destination="*"/> <!-- missing privilege="foo" -->
+ </policy>
+</busconfig>
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <user>mybususer</user>
+ <listen>unix:path=/foo/bar</listen>
+ <policy context="default">
+ <check privilege="foo" send_destination="*"/>
+ </policy>
+</busconfig>
--- /dev/null
+<!-- Bus that listens on a debug pipe and doesn't create any restrictions -->
+
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <listen>debug-pipe:name=test-server</listen>
+ <listen>@TEST_LISTEN@</listen>
+ <servicedir>@DBUS_TEST_DATA@/valid-service-files</servicedir>
+ <policy context="default">
+ <allow send_interface="*"/>
+ <allow receive_interface="*"/>
+ <allow own="*"/>
+ <allow user="*"/>
+
+ <deny send_interface="org.freedesktop.TestSuite" send_member="Echo"/>
+ <check privilege="foo" send_interface="org.freedesktop.TestSuite" send_member="Echo"/>
+ </policy>
+</busconfig>