From 0fce7ba3356b567f380c89e84c82a3a1d6dca612 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Wed, 19 Jul 2017 17:56:38 +0100 Subject: [PATCH] policy: Add max_fds, min_fds qualifiers for send, receive rules Signed-off-by: Simon McVittie --- bus/config-parser.c | 77 ++++++++++++++++++++++++++++++++++++++++++-- bus/policy.c | 32 +++++++++++++++++- bus/policy.h | 4 +++ dbus/dbus-message-internal.h | 1 + dbus/dbus-message-util.c | 16 +++++++++ dbus/dbus-message.c | 4 +-- doc/dbus-daemon.1.xml.in | 20 ++++++++++++ 7 files changed, 148 insertions(+), 6 deletions(-) diff --git a/bus/config-parser.c b/bus/config-parser.c index 52576e9d..7f095bd5 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -1276,6 +1276,43 @@ start_busconfig_child (BusConfigParser *parser, } } +/* + * Parse an attribute named name, whose content is content, or NULL if + * missing. It is meant to be a (long) integer between min and max inclusive. + * If it is missing, use def as the default value (which does not + * necessarily need to be between min and max). + */ +static dbus_bool_t +parse_int_attribute (const char *name, + const char *content, + long min, + long max, + long def, + long *value, + DBusError *error) +{ + DBusString parse_string; + + *value = def; + + if (content == NULL) + return TRUE; + + _dbus_string_init_const (&parse_string, content); + + if (!_dbus_string_parse_int (&parse_string, 0, value, NULL) || + *value < min || *value > max) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Bad value \"%s\" for %s attribute, must be an " + "integer in range %ld to %ld inclusive", + content, name, min, max); + return FALSE; + } + + return TRUE; +} + static dbus_bool_t append_rule_from_element (BusConfigParser *parser, const char *element_name, @@ -1311,6 +1348,10 @@ append_rule_from_element (BusConfigParser *parser, /* Group: message-matching modifiers that can go on send_ or receive_ */ const char *eavesdrop; + const char *max_fds_attr; + long max_fds = DBUS_MAXIMUM_MESSAGE_UNIX_FDS; + const char *min_fds_attr; + long min_fds = 0; /* TRUE if any message-matching modifier is present */ dbus_bool_t any_message_attribute; @@ -1340,6 +1381,8 @@ append_rule_from_element (BusConfigParser *parser, "receive_path", &receive_path, "receive_type", &receive_type, "eavesdrop", &eavesdrop, + "max_fds", &max_fds_attr, + "min_fds", &min_fds_attr, "send_requested_reply", &send_requested_reply, "receive_requested_reply", &receive_requested_reply, "own", &own, @@ -1373,7 +1416,9 @@ append_rule_from_element (BusConfigParser *parser, (!any_send_attribute && eavesdrop != NULL)); any_message_attribute = (any_send_attribute || any_receive_attribute || - eavesdrop != NULL); + eavesdrop != NULL || + max_fds_attr != NULL || + min_fds_attr != NULL); if (!(any_send_attribute || any_receive_attribute || @@ -1518,7 +1563,19 @@ append_rule_from_element (BusConfigParser *parser, "send_requested_reply", send_requested_reply); return FALSE; } - + + /* Matching only messages with DBUS_MAXIMUM_MESSAGE_UNIX_FDS or fewer + * fds is the same as matching all messages, so we always set a maximum, + * but perhaps an unrealistically high one. */ + if (!parse_int_attribute ("max_fds", max_fds_attr, + 0, DBUS_MAXIMUM_MESSAGE_UNIX_FDS, + DBUS_MAXIMUM_MESSAGE_UNIX_FDS, &max_fds, + error) || + !parse_int_attribute ("min_fds", min_fds_attr, + 0, DBUS_MAXIMUM_MESSAGE_UNIX_FDS, 0, &min_fds, + error)) + return FALSE; + rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow); if (rule == NULL) goto nomem; @@ -1550,6 +1607,9 @@ append_rule_from_element (BusConfigParser *parser, rule->d.send.member = _dbus_strdup (send_member); rule->d.send.error = _dbus_strdup (send_error); rule->d.send.destination = _dbus_strdup (send_destination); + rule->d.send.max_fds = max_fds; + rule->d.send.min_fds = min_fds; + if (send_path && rule->d.send.path == NULL) goto nomem; if (send_interface && rule->d.send.interface == NULL) @@ -1611,7 +1671,16 @@ append_rule_from_element (BusConfigParser *parser, "receive_requested_reply", receive_requested_reply); return FALSE; } - + + if (!parse_int_attribute ("max_fds", max_fds_attr, + 0, DBUS_MAXIMUM_MESSAGE_UNIX_FDS, + DBUS_MAXIMUM_MESSAGE_UNIX_FDS, &max_fds, + error) || + !parse_int_attribute ("min_fds", min_fds_attr, + 0, DBUS_MAXIMUM_MESSAGE_UNIX_FDS, 0, &min_fds, + error)) + return FALSE; + rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow); if (rule == NULL) goto nomem; @@ -1628,6 +1697,8 @@ append_rule_from_element (BusConfigParser *parser, rule->d.receive.member = _dbus_strdup (receive_member); rule->d.receive.error = _dbus_strdup (receive_error); rule->d.receive.origin = _dbus_strdup (receive_sender); + rule->d.receive.max_fds = max_fds; + rule->d.receive.min_fds = min_fds; if (receive_path && rule->d.receive.path == NULL) goto nomem; diff --git a/bus/policy.c b/bus/policy.c index d8d82f41..a37be804 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -29,6 +29,7 @@ #include #include #include +#include BusPolicyRule* bus_policy_rule_new (BusPolicyRuleType type, @@ -1063,6 +1064,20 @@ bus_client_policy_check_can_send (BusClientPolicy *policy, } } + if (rule->d.send.min_fds > 0 || + rule->d.send.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) + { + unsigned int n_fds = _dbus_message_get_n_unix_fds (message); + + if (n_fds < rule->d.send.min_fds || n_fds > rule->d.send.max_fds) + { + _dbus_verbose (" (policy) skipping rule because message has %u fds " + "and that is outside range [%u,%u]", + n_fds, rule->d.send.min_fds, rule->d.send.max_fds); + continue; + } + } + /* Use this rule */ allowed = rule->allow; *log = rule->d.send.log; @@ -1263,7 +1278,22 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy, } } } - + + if (rule->d.receive.min_fds > 0 || + rule->d.receive.max_fds < DBUS_MAXIMUM_MESSAGE_UNIX_FDS) + { + unsigned int n_fds = _dbus_message_get_n_unix_fds (message); + + if (n_fds < rule->d.receive.min_fds || n_fds > rule->d.receive.max_fds) + { + _dbus_verbose (" (policy) skipping rule because message has %u fds " + "and that is outside range [%u,%u]", + n_fds, rule->d.receive.min_fds, + rule->d.receive.max_fds); + continue; + } + } + /* Use this rule */ allowed = rule->allow; (*toggles)++; diff --git a/bus/policy.h b/bus/policy.h index c5275a7b..ec43ffa5 100644 --- a/bus/policy.h +++ b/bus/policy.h @@ -70,6 +70,8 @@ struct BusPolicyRule char *member; char *error; char *destination; + unsigned int max_fds; + unsigned int min_fds; unsigned int eavesdrop : 1; unsigned int requested_reply : 1; unsigned int log : 1; @@ -86,6 +88,8 @@ struct BusPolicyRule char *member; char *error; char *origin; + unsigned int max_fds; + unsigned int min_fds; unsigned int eavesdrop : 1; unsigned int requested_reply : 1; } receive; diff --git a/dbus/dbus-message-internal.h b/dbus/dbus-message-internal.h index 6f113169..f8fe383f 100644 --- a/dbus/dbus-message-internal.h +++ b/dbus/dbus-message-internal.h @@ -55,6 +55,7 @@ void _dbus_message_get_unix_fds (DBusMessage *message, const int **fds, unsigned *n_fds); +unsigned int _dbus_message_get_n_unix_fds (DBusMessage *message); void _dbus_message_lock (DBusMessage *message); void _dbus_message_unlock (DBusMessage *message); dbus_bool_t _dbus_message_add_counter (DBusMessage *message, diff --git a/dbus/dbus-message-util.c b/dbus/dbus-message-util.c index bedf6b4f..ebf00e27 100644 --- a/dbus/dbus-message-util.c +++ b/dbus/dbus-message-util.c @@ -45,6 +45,22 @@ * @{ */ +/** + * Gets the number of unix fds attached to this message. + * + * @param message the message + * @returns the number of file descriptors + */ +unsigned int +_dbus_message_get_n_unix_fds (DBusMessage *message) +{ +#ifdef HAVE_UNIX_FD_PASSING + return message->n_unix_fds; +#else + return 0; +#endif +} + #ifdef DBUS_ENABLE_EMBEDDED_TESTS /** * Reads arguments from a message iterator given a variable argument diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 43b3a9fb..a25d562e 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -244,8 +244,8 @@ _dbus_message_get_network_data (DBusMessage *message, * message is locked (with dbus_message_lock()). * * @param message the message. - * @param fds return location of unix fd array - * @param n_fds return number of entries in array + * @param fds return location of unix fd array, or #NULL + * @param n_fds return number of entries in array, or #NULL */ void _dbus_message_get_unix_fds(DBusMessage *message, const int **fds, diff --git a/doc/dbus-daemon.1.xml.in b/doc/dbus-daemon.1.xml.in index 067fdcd5..b029232d 100644 --- a/doc/dbus-daemon.1.xml.in +++ b/doc/dbus-daemon.1.xml.in @@ -906,6 +906,13 @@ rules in the config file allow it). recipient, and any broadcast message). + + The eavesdrop, min_fds and + max_fds attributes are modifiers that can be applied + to either send_* or receive_* + rules, and are documented below. + + send_destination and receive_sender rules mean that messages may not be sent to or received from the *owner* of the given name, not that they may not be sent *to that name*. That is, if a connection @@ -969,6 +976,19 @@ the rule matches only when the reply was not requested. [send|receive]_requested_reply="true" indicates that the rule applies always, regardless of pending reply state. + + The min_fds and max_fds attributes + modify either send_* or receive_* + rules. A rule with the min_fds attribute only matches + messages if they have at least that many Unix file descriptors attached. + Conversely, a rule with the max_fds attribute only + matches messages if they have no more than that many file descriptors + attached. In practice, rules with these attributes will most commonly + take the form + <allow send_destination="…" max_fds="0"/>, + <deny send_destination="…" min_fds="1"/> or + <deny receive_sender="*" min_fds="1"/>. + Rules with the user or group -- 2.13.3