policy: Add max_fds, min_fds qualifiers for send, receive rules
authorSimon McVittie <smcv@collabora.com>
Wed, 19 Jul 2017 16:56:38 +0000 (17:56 +0100)
committerSimon McVittie <smcv@debian.org>
Fri, 28 Jul 2017 10:36:51 +0000 (11:36 +0100)
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=101848
Reviewed-by: Thiago Macieira <thiago@kde.org>
[smcv: Revert an incorrect comment change]
Signed-off-by: Simon McVittie <smcv@collabora.com>
bus/config-parser.c
bus/policy.c
bus/policy.h
dbus/dbus-message-internal.h
dbus/dbus-message-util.c
doc/dbus-daemon.1.xml.in

index 52576e9..7f095bd 100644 (file)
@@ -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;
index d8d82f4..a37be80 100644 (file)
@@ -29,6 +29,7 @@
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-internals.h>
+#include <dbus/dbus-message-internal.h>
 
 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)++;
index c5275a7..ec43ffa 100644 (file)
@@ -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;
index 6f11316..f8fe383 100644 (file)
@@ -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,
index bedf6b4..ebf00e2 100644 (file)
  * @{
  */
 
+/**
+ * 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
index 067fdcd..b029232 100644 (file)
@@ -906,6 +906,13 @@ rules in the config file allow it).</para>
   recipient, and any broadcast message).
 </para>
 
+<para>
+  The <literal>eavesdrop</literal>, <literal>min_fds</literal> and
+  <literal>max_fds</literal> attributes are modifiers that can be applied
+  to either <literal>send_</literal>* or <literal>receive_</literal>*
+  rules, and are documented below.
+</para>
+
 <para>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.</para>
 
+<para>
+  The <literal>min_fds</literal> and <literal>max_fds</literal> attributes
+  modify either <literal>send_</literal>* or <literal>receive_</literal>*
+  rules. A rule with the <literal>min_fds</literal> attribute only matches
+  messages if they have at least that many Unix file descriptors attached.
+  Conversely, a rule with the <literal>max_fds</literal> 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
+  <literal>&lt;allow send_destination="&hellip;" max_fds="0"/&gt;</literal>,
+  <literal>&lt;deny send_destination="&hellip;" min_fds="1"/&gt;</literal> or
+  <literal>&lt;deny receive_sender="*" min_fds="1"/&gt;</literal>.
+</para>
 
 <para>
   Rules with the <literal>user</literal> or <literal>group</literal>