Do not allow eavedropping unless rule owner explicitely declare it
authorCosimo Alfarano <cosimo.alfarano@collabora.co.uk>
Mon, 11 Jul 2011 16:03:00 +0000 (17:03 +0100)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Mon, 11 Jul 2011 16:03:20 +0000 (17:03 +0100)
Adds "eavesdrop=true" as a match rule, meaning that the owner
intend to eavedrop.
Otherwise the owner will receive only broadcasted messages and the ones
meant to be delivered to it.

[plus a typo fix in an error message -smcv]

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=37890
Bug-NB: NB#269748
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
bus/signals.c
bus/signals.h

index 62881b5..28506d3 100644 (file)
@@ -215,6 +215,20 @@ match_rule_to_string (BusMatchRule *rule)
         goto nomem;
     }
 
+  if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+    {
+      if (_dbus_string_get_length (&str) > 0)
+        {
+          if (!_dbus_string_append (&str, ","))
+            goto nomem;
+        }
+
+      if (!_dbus_string_append_printf (&str, "eavesdrop='%s'",
+            (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) ?
+            "true" : "false"))
+        goto nomem;
+    }
+
   if (rule->flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -354,6 +368,16 @@ bus_match_rule_set_destination (BusMatchRule *rule,
   return TRUE;
 }
 
+void
+bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
+                                            dbus_bool_t is_eavesdropping)
+{
+  if (is_eavesdropping)
+    rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+  else
+    rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+}
+
 dbus_bool_t
 bus_match_rule_set_path (BusMatchRule *rule,
                          const char   *path,
@@ -1030,6 +1054,31 @@ bus_match_rule_parse (DBusConnection   *matches_go_to,
               goto failed;
             }
         }
+      else if (strcmp (key, "eavesdrop") == 0)
+        {
+          /* do not detect "eavesdrop" being used more than once in rule:
+           * 1) it's not possible, it's only in the flags
+           * 2) it might be used twice to disable eavesdropping when it's
+           * automatically added (eg dbus-monitor/bustle) */
+
+          /* we accept only "true|false" as possible values */
+          if ((strcmp (value, "true") == 0))
+            {
+              bus_match_rule_set_client_is_eavesdropping (rule, TRUE);
+            }
+          else if (strcmp (value, "false") == 0)
+            {
+              bus_match_rule_set_client_is_eavesdropping (rule, FALSE);
+            }
+          else
+            {
+              dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
+                              "eavesdrop='%s' is invalid, "
+                              "it should be 'true' or 'false'\n",
+                              value);
+              goto failed;
+            }
+        }
       else if (strncmp (key, "arg", 3) == 0)
         {
           if (!bus_match_rule_parse_arg_match (rule, key, &tmp_str, error))
@@ -1352,6 +1401,9 @@ match_rule_equal (BusMatchRule *a,
       strcmp (a->destination, b->destination) != 0)
     return FALSE;
 
+  /* we already compared the value of flags, and
+   * BUS_MATCH_CLIENT_IS_EAVESDROPPING does not have another struct member */
+
   if (a->flags & BUS_MATCH_ARGS)
     {
       int i;
@@ -1619,6 +1671,7 @@ match_rule_matches (BusMatchRule    *rule,
                     DBusMessage     *message,
                     BusMatchFlags    already_matched)
 {
+  dbus_bool_t wants_to_eavesdrop = FALSE;
   int flags;
 
   /* All features of the match rule are AND'd together,
@@ -1633,6 +1686,9 @@ match_rule_matches (BusMatchRule    *rule,
   /* Don't bother re-matching features we've already checked implicitly. */
   flags = rule->flags & (~already_matched);
 
+  if (flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+    wants_to_eavesdrop = TRUE;
+
   if (flags & BUS_MATCH_MESSAGE_TYPE)
     {
       _dbus_assert (rule->message_type != DBUS_MESSAGE_TYPE_INVALID);
@@ -1686,6 +1742,24 @@ match_rule_matches (BusMatchRule    *rule,
         }
     }
 
+  /* Note: this part is relevant for eavesdropper rules:
+   * Two cases:
+   * 1) rule has a destination to be matched
+   *   (flag BUS_MATCH_DESTINATION present). Rule will match if:
+   *   - rule->destination matches the addressed_recipient
+   *   AND
+   *   - wants_to_eavesdrop=TRUE
+   *
+   *   Note: (the case in which addressed_recipient is the actual rule owner
+   *   is handled elsewere in dispatch.c:bus_dispatch_matches().
+   *
+   * 2) rule has no destination. Rule will match if:
+   *    - message has no specified destination (ie broadcasts)
+   *      (Note: this will rule out unicast method calls and unicast signals,
+   *      fixing FDO#269748)
+   *    OR
+   *    - wants_to_eavesdrop=TRUE (destination-catch-all situation)
+   */
   if (flags & BUS_MATCH_DESTINATION)
     {
       const char *destination;
@@ -1694,6 +1768,12 @@ match_rule_matches (BusMatchRule    *rule,
 
       destination = dbus_message_get_destination (message);
       if (destination == NULL)
+        /* broadcast, but this rule specified a destination: no match */
+        return FALSE;
+
+      /* rule owner does not intend to eavesdrop: we'll deliver only msgs
+       * directed to it, NOT MATCHING */
+      if (!wants_to_eavesdrop)
         return FALSE;
 
       if (addressed_recipient == NULL)
@@ -1707,6 +1787,19 @@ match_rule_matches (BusMatchRule    *rule,
           if (!connection_is_primary_owner (addressed_recipient, rule->destination))
             return FALSE;
         }
+    } else { /* no destination in rule */
+        dbus_bool_t msg_is_broadcast;
+
+        _dbus_assert (rule->destination == NULL);
+
+        msg_is_broadcast = (dbus_message_get_destination (message) == NULL);
+
+        if (!wants_to_eavesdrop && !msg_is_broadcast)
+          return FALSE;
+
+        /* if we are here rule owner intends to eavesdrop
+         * OR
+         * message is being broadcasted */
     }
 
   if (flags & BUS_MATCH_PATH)
index 5b086f0..a71d2e4 100644 (file)
 
 typedef enum
 {
-  BUS_MATCH_MESSAGE_TYPE   = 1 << 0,
-  BUS_MATCH_INTERFACE      = 1 << 1,
-  BUS_MATCH_MEMBER         = 1 << 2,
-  BUS_MATCH_SENDER         = 1 << 3,
-  BUS_MATCH_DESTINATION    = 1 << 4,
-  BUS_MATCH_PATH           = 1 << 5,
-  BUS_MATCH_ARGS           = 1 << 6,
-  BUS_MATCH_PATH_NAMESPACE = 1 << 7
+  BUS_MATCH_MESSAGE_TYPE            = 1 << 0,
+  BUS_MATCH_INTERFACE               = 1 << 1,
+  BUS_MATCH_MEMBER                  = 1 << 2,
+  BUS_MATCH_SENDER                  = 1 << 3,
+  BUS_MATCH_DESTINATION             = 1 << 4,
+  BUS_MATCH_PATH                    = 1 << 5,
+  BUS_MATCH_ARGS                    = 1 << 6,
+  BUS_MATCH_PATH_NAMESPACE          = 1 << 7,
+  BUS_MATCH_CLIENT_IS_EAVESDROPPING = 1 << 8
 } BusMatchFlags;
 
 BusMatchRule* bus_match_rule_new   (DBusConnection *matches_go_to);
@@ -64,6 +65,14 @@ dbus_bool_t bus_match_rule_set_arg          (BusMatchRule     *rule,
                                              dbus_bool_t       is_path,
                                              dbus_bool_t       is_namespace);
 
+/* Calling this methods a client declares that it is creating a rule which
+ * needs to eavesdrop (e.g., dbus-monitor), any other created rules not
+ * setting themselves as eavesdropping won't receive any message not addressed
+ * to them, when eavedrop is enabled in the policy.  On the other hand, when
+ * eavedrop is not enabled in policy, this method won't have any effect */
+void bus_match_rule_set_client_is_eavesdropping (BusMatchRule     *rule,
+                                                 dbus_bool_t is_eavesdropping);
+
 BusMatchRule* bus_match_rule_parse (DBusConnection   *matches_go_to,
                                     const DBusString *rule_text,
                                     DBusError        *error);