bus/policy: extract check_* functions 87/234087/2
authorAdrian Szyndela <adrian.s@samsung.com>
Wed, 20 May 2020 12:03:10 +0000 (14:03 +0200)
committerAdrian Szyndela <adrian.s@samsung.com>
Mon, 1 Jun 2020 09:59:55 +0000 (11:59 +0200)
No functional changes, just moving code around.

This extracts check_send_rule, check_receive_rule,
and check_own_rule from their own respective
bus_client_policy_can_check_* functions.

Change-Id: Ice4b2b96054b33a376bc3f48df29447747e7980e

bus/policy.c

index 6f27d1c..1f3cee8 100644 (file)
@@ -866,281 +866,296 @@ bus_policy_rule_to_string (BusPolicyRule *rule,
   return TRUE;
 }
 
-BusResult
-bus_client_policy_check_can_send (DBusConnection      *sender,
-                                  BusClientPolicy     *policy,
-                                  BusRegistry         *registry,
-                                  dbus_bool_t          requested_reply,
-                                  DBusConnection      *addressed_recipient,
-                                  DBusConnection      *receiver,
-                                  DBusMessage         *message,
-                                  dbus_int32_t        *toggles,
-                                  dbus_bool_t         *log,
-                                  const char         **privilege_param,
-                                  BusDeferredMessage **deferred_message,
-                                  char               **out_rule)
+static dbus_bool_t
+check_send_rule (const BusPolicyRule  *rule,
+                 BusRegistry          *registry,
+                 dbus_bool_t           requested_reply,
+                 DBusConnection       *receiver,
+                 DBusMessage          *message,
+                 BusResult            *result,
+                 const char          **privilege)
 {
-  DBusList *link;
-  BusResult result;
-  const char *privilege;
-  BusPolicyRule *matched_rule = NULL;
-
-  /* policy->rules is in the order the rules appeared
-   * in the config file, i.e. last rule that applies wins
+  /* Rule is skipped if it specifies a different
+   * message name from the message, or a different
+   * destination from the message
    */
+  if (rule->type != BUS_POLICY_RULE_SEND)
+    {
+      _dbus_verbose ("  (policy) skipping non-send rule\n");
+      return FALSE;
+    }
 
-  _dbus_verbose ("  (policy) checking send rules\n");
-  *toggles = 0;
-  
-  result = BUS_RESULT_FALSE;
-  link = _dbus_list_get_first_link (&policy->rules);
-  while (link != NULL)
+  if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID)
     {
-      BusPolicyRule *rule = link->data;
+      if (dbus_message_get_type (message) != rule->d.send.message_type)
+        {
+          _dbus_verbose ("  (policy) skipping rule for different message type\n");
+          return FALSE;
+        }
+    }
 
-      link = _dbus_list_get_next_link (&policy->rules, link);
-      
-      /* Rule is skipped if it specifies a different
-       * message name from the message, or a different
-       * destination from the message
+  /* If it's a reply, the requested_reply flag kicks in */
+  if (dbus_message_get_reply_serial (message) != 0)
+    {
+      /* 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 (rule->type != BUS_POLICY_RULE_SEND)
+      if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
         {
-          _dbus_verbose ("  (policy) skipping non-send rule\n");
-          continue;
+          _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");
+          return FALSE;
         }
 
-      if (rule->d.send.message_type != DBUS_MESSAGE_TYPE_INVALID)
+      /* for deny, requested_reply=false means the rule applies only
+       * when the reply was not requested. requested_reply=true means the
+       * rule always applies.
+       */
+      if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.send.requested_reply)
         {
-          if (dbus_message_get_type (message) != rule->d.send.message_type)
-            {
-              _dbus_verbose ("  (policy) skipping rule for different message type\n");
-              continue;
-            }
+          _dbus_verbose ("  (policy) skipping deny rule since it only applies to unrequested replies\n");
+          return FALSE;
         }
+    }
 
-      /* If it's a reply, the requested_reply flag kicks in */
-      if (dbus_message_get_reply_serial (message) != 0)
+  if (rule->d.send.path != NULL)
+    {
+      if (dbus_message_get_path (message) != NULL &&
+          strcmp (dbus_message_get_path (message),
+                  rule->d.send.path) != 0)
         {
-          /* 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->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
-            {
-              _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;
-            }
-
-          /* for deny, requested_reply=false means the rule applies only
-           * when the reply was not requested. requested_reply=true means the
-           * rule always applies.
-           */
-          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;
-            }
+          _dbus_verbose ("  (policy) skipping rule for different path\n");
+          return FALSE;
         }
+    }
+
+  if (rule->d.send.interface != NULL)
+    {
+      /* 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). 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 (rule->d.send.path != NULL)
+      if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
+          (!no_interface &&
+           strcmp (dbus_message_get_interface (message),
+                   rule->d.send.interface) != 0))
         {
-          if (dbus_message_get_path (message) != NULL &&
-              strcmp (dbus_message_get_path (message),
-                      rule->d.send.path) != 0)
-            {
-              _dbus_verbose ("  (policy) skipping rule for different path\n");
-              continue;
-            }
+          _dbus_verbose ("  (policy) skipping rule for different interface\n");
+          return FALSE;
         }
-      
-      if (rule->d.send.interface != NULL)
-        {
-          /* 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). 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->access != BUS_POLICY_RULE_ACCESS_DENY) ||
-              (!no_interface && 
-               strcmp (dbus_message_get_interface (message),
-                       rule->d.send.interface) != 0))
-            {
-              _dbus_verbose ("  (policy) skipping rule for different interface\n");
-              continue;
-            }
+  if (rule->d.send.member != NULL)
+    {
+      if (dbus_message_get_member (message) != NULL &&
+          strcmp (dbus_message_get_member (message),
+                  rule->d.send.member) != 0)
+        {
+          _dbus_verbose ("  (policy) skipping rule for different member\n");
+          return FALSE;
         }
+    }
 
-      if (rule->d.send.member != NULL)
+  if (rule->d.send.error != NULL)
+    {
+      if (dbus_message_get_error_name (message) != NULL &&
+          strcmp (dbus_message_get_error_name (message),
+                  rule->d.send.error) != 0)
         {
-          if (dbus_message_get_member (message) != NULL &&
-              strcmp (dbus_message_get_member (message),
-                      rule->d.send.member) != 0)
-            {
-              _dbus_verbose ("  (policy) skipping rule for different member\n");
-              continue;
-            }
+          _dbus_verbose ("  (policy) skipping rule for different error name\n");
+          return FALSE;
         }
+    }
 
-      if (rule->d.send.error != NULL)
+  if (rule->d.send.broadcast != BUS_POLICY_TRISTATE_ANY)
+    {
+      if (dbus_message_get_destination (message) == NULL &&
+          dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
         {
-          if (dbus_message_get_error_name (message) != NULL &&
-              strcmp (dbus_message_get_error_name (message),
-                      rule->d.send.error) != 0)
+          /* it's a broadcast */
+          if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_FALSE)
             {
-              _dbus_verbose ("  (policy) skipping rule for different error name\n");
-              continue;
+              _dbus_verbose ("  (policy) skipping rule because message is a broadcast\n");
+              return FALSE;
             }
         }
+      /* else it isn't a broadcast: there is some destination */
+      else if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_TRUE)
+        {
+          _dbus_verbose ("  (policy) skipping rule because message is not a broadcast\n");
+          return FALSE;
+        }
+    }
 
-      if (rule->d.send.broadcast != BUS_POLICY_TRISTATE_ANY)
+  if (rule->d.send.destination != NULL)
+    {
+      if (!rule->d.send.destination_prefix)
         {
-          if (dbus_message_get_destination (message) == NULL &&
-              dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_SIGNAL)
+          /* receiver can be NULL for messages that are sent to the
+           * message bus itself, we check the strings in that case as
+           * built-in services don't have a DBusConnection but messages
+           * to them have a destination service name.
+           *
+           * Similarly, receiver can be NULL when we're deciding whether
+           * activation should be allowed; we make the authorization decision
+           * on the assumption that the activated service will have the
+           * requested name and no others.
+           */
+          if (receiver == NULL)
             {
-              /* it's a broadcast */
-              if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_FALSE)
+              if (!dbus_message_has_destination (message,
+                                                 rule->d.send.destination))
                 {
-                  _dbus_verbose ("  (policy) skipping rule because message is a broadcast\n");
-                  continue;
+                  _dbus_verbose ("  (policy) skipping rule because message dest is not %s\n",
+                                 rule->d.send.destination);
+                  return FALSE;
                 }
             }
-          /* else it isn't a broadcast: there is some destination */
-          else if (rule->d.send.broadcast == BUS_POLICY_TRISTATE_TRUE)
+          else
             {
-              _dbus_verbose ("  (policy) skipping rule because message is not a broadcast\n");
-              continue;
-            }
-        }
+              DBusString str;
+              BusService *service;
 
-      if (rule->d.send.destination != NULL)
-        {
-          if (!rule->d.send.destination_prefix)
-            {
-              /* receiver can be NULL for messages that are sent to the
-               * message bus itself, we check the strings in that case as
-               * built-in services don't have a DBusConnection but messages
-               * to them have a destination service name.
-               *
-               * Similarly, receiver can be NULL when we're deciding whether
-               * activation should be allowed; we make the authorization decision
-               * on the assumption that the activated service will have the
-               * requested name and no others.
-               */
-              if (receiver == NULL)
+              _dbus_string_init_const (&str, rule->d.send.destination);
+
+              service = bus_registry_lookup (registry, &str);
+              if (service == NULL)
                 {
-                  if (!dbus_message_has_destination (message,
-                                                     rule->d.send.destination))
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because message dest is not %s\n",
-                                     rule->d.send.destination);
-                      continue;
-                    }
+                  _dbus_verbose ("  (policy) skipping rule because dest %s doesn't exist\n",
+                                 rule->d.send.destination);
+                  return FALSE;
                 }
-              else
+
+              if (!bus_service_has_owner (service, receiver))
                 {
-                  DBusString str;
-                  BusService *service;
-
-                  _dbus_string_init_const (&str, rule->d.send.destination);
-
-                  service = bus_registry_lookup (registry, &str);
-                  if (service == NULL)
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because dest %s doesn't exist\n",
-                                     rule->d.send.destination);
-                      continue;
-                    }
-
-                  if (!bus_service_has_owner (service, receiver))
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because dest %s isn't owned by receiver\n",
-                                     rule->d.send.destination);
-                      continue;
-                    }
+                  _dbus_verbose ("  (policy) skipping rule because dest %s isn't owned by receiver\n",
+                                 rule->d.send.destination);
+                  return FALSE;
                 }
             }
-          else if (rule->d.send.destination_prefix)
+        }
+      else if (rule->d.send.destination_prefix)
+        {
+          /* receiver can be NULL - the same as in !send.destination_prefix */
+          if (receiver == NULL)
             {
-              /* receiver can be NULL - the same as in !send.destination_prefix */
-              if (receiver == NULL)
+              const char *destination = dbus_message_get_destination (message);
+              DBusString dest_name;
+
+              if (destination == NULL)
                 {
-                  const char *destination = dbus_message_get_destination (message);
-                  DBusString dest_name;
-
-                  if (destination == NULL)
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because message has no dest\n");
-                      continue;
-                    }
-
-                  _dbus_string_init_const (&dest_name, destination);
-
-                  if (!_dbus_string_starts_with_words_c_str (&dest_name,
-                                                             rule->d.send.destination,
-                                                             '.'))
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because message dest doesn't start with %s\n",
-                                     rule->d.send.destination);
-                      continue;
-                    }
+                  _dbus_verbose ("  (policy) skipping rule because message has no dest\n");
+                  return FALSE;
                 }
-              else
+
+              _dbus_string_init_const (&dest_name, destination);
+
+              if (!_dbus_string_starts_with_words_c_str (&dest_name,
+                                                         rule->d.send.destination,
+                                                         '.'))
                 {
-                  if (!bus_connection_is_service_owner_by_prefix (receiver,
-                                                                  rule->d.send.destination))
-                    {
-                      _dbus_verbose ("  (policy) skipping rule because no dest with prefix %s is owned by receiver\n",
-                                     rule->d.send.destination);
-                      continue;
-                    }
+                  _dbus_verbose ("  (policy) skipping rule because message dest doesn't start with %s\n",
+                                 rule->d.send.destination);
+                  return FALSE;
                 }
             }
-        }
-
-      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)
+          else
             {
-              _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;
+              if (!bus_connection_is_service_owner_by_prefix (receiver,
+                                                              rule->d.send.destination))
+                {
+                  _dbus_verbose ("  (policy) skipping rule because no dest with prefix %s is owned by receiver\n",
+                                 rule->d.send.destination);
+                  return FALSE;
+                }
             }
         }
+    }
 
-      /* Use this rule */
-      switch (rule->access)
+  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)
         {
-        case BUS_POLICY_RULE_ACCESS_ALLOW:
-          result = BUS_RESULT_TRUE;
-          break;
-        case BUS_POLICY_RULE_ACCESS_DENY:
-          result = BUS_RESULT_FALSE;
-          break;
-        case BUS_POLICY_RULE_ACCESS_CHECK:
-          result = BUS_RESULT_LATER;
-          privilege = rule->privilege;
-          break;
+          _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);
+          return FALSE;
         }
+    }
+
+  /* Use this rule */
+  switch (rule->access)
+  {
+    case BUS_POLICY_RULE_ACCESS_ALLOW:
+      *result = BUS_RESULT_TRUE;
+      break;
+    case BUS_POLICY_RULE_ACCESS_DENY:
+      *result = BUS_RESULT_FALSE;
+      break;
+    case BUS_POLICY_RULE_ACCESS_CHECK:
+      *result = BUS_RESULT_LATER;
+      *privilege = rule->privilege;
+      break;
+  }
+
+  return TRUE;
+}
+
+BusResult
+bus_client_policy_check_can_send (DBusConnection      *sender,
+                                  BusClientPolicy     *policy,
+                                  BusRegistry         *registry,
+                                  dbus_bool_t          requested_reply,
+                                  DBusConnection      *addressed_recipient,
+                                  DBusConnection      *receiver,
+                                  DBusMessage         *message,
+                                  dbus_int32_t        *toggles,
+                                  dbus_bool_t         *log,
+                                  const char         **privilege_param,
+                                  BusDeferredMessage **deferred_message,
+                                  char               **out_rule)
+{
+  DBusList *link;
+  BusResult result;
+  const char *privilege;
+  BusPolicyRule *matched_rule = NULL;
+
+  /* policy->rules is in the order the rules appeared
+   * in the config file, i.e. last rule that applies wins
+   */
+
+  _dbus_verbose ("  (policy) checking send rules\n");
+  *toggles = 0;
+
+  result = BUS_RESULT_FALSE;
+  link = _dbus_list_get_first_link (&policy->rules);
+  while (link != NULL)
+    {
+      const BusPolicyRule *rule = link->data;
+
+      link = _dbus_list_get_next_link (&policy->rules, link);
 
-      *log = rule->d.send.log;
-      (*toggles)++;
-      matched_rule = rule;
+      if (check_send_rule (rule, registry, requested_reply, receiver, message,
+                           &result, &privilege))
+        {
+          *log = rule->d.send.log;
+          (*toggles)++;
+          matched_rule = (BusPolicyRule *)rule;
 
-      _dbus_verbose ("  (policy) used rule, result now = %d\n",
-                     result);
+          _dbus_verbose ("  (policy) used rule, result now = %d\n",
+                         result);
+        }
     }
 
   if (result == BUS_RESULT_LATER)
@@ -1169,238 +1184,255 @@ bus_client_policy_check_can_send (DBusConnection      *sender,
   return result;
 }
 
-/* See docs on what the args mean on bus_context_check_security_policy()
- * comment
- */
-BusResult
-bus_client_policy_check_can_receive (BusClientPolicy     *policy,
-                                     BusRegistry         *registry,
-                                     dbus_bool_t          requested_reply,
-                                     DBusConnection      *sender,
-                                     DBusConnection      *addressed_recipient,
-                                     DBusConnection      *proposed_recipient,
-                                     DBusMessage         *message,
-                                     dbus_int32_t        *toggles,
-                                     const char         **privilege_param,
-                                     BusDeferredMessage **deferred_message,
-                                     char               **out_rule)
+static dbus_bool_t
+check_receive_rule (const BusPolicyRule  *rule,
+                    BusRegistry          *registry,
+                    dbus_bool_t           requested_reply,
+                    DBusConnection       *sender,
+                    DBusMessage          *message,
+                    dbus_bool_t           eavesdropping,
+                    BusResult            *result,
+                    const char          **privilege)
 {
-  DBusList *link;
-  dbus_bool_t eavesdropping;
-  BusResult result;
-  const char *privilege;
-  BusPolicyRule *matched_rule = NULL;
+  if (rule->type != BUS_POLICY_RULE_RECEIVE)
+    {
+      _dbus_verbose ("  (policy) skipping non-receive rule\n");
+      return FALSE;
+    }
 
-  eavesdropping =
-    addressed_recipient != proposed_recipient &&
-    dbus_message_get_destination (message) != NULL;
-  
-  /* policy->rules is in the order the rules appeared
-   * in the config file, i.e. last rule that applies wins
+  if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID)
+    {
+      if (dbus_message_get_type (message) != rule->d.receive.message_type)
+        {
+          _dbus_verbose ("  (policy) skipping rule for different message type\n");
+          return FALSE;
+        }
+    }
+
+
+  /* for allow or check, eavesdrop=false means the rule doesn't apply when
+   * eavesdropping. eavesdrop=true means the rule always applies
    */
+  if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop)
+    {
+      _dbus_verbose ("  (policy) skipping %s rule since it doesn't apply to eavesdropping\n",
+          rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
+      return FALSE;
+    }
 
-  _dbus_verbose ("  (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
-  *toggles = 0;
-  
-  result = BUS_RESULT_FALSE;
-  link = _dbus_list_get_first_link (&policy->rules);
-  while (link != NULL)
+  /* for deny, eavesdrop=true means the rule applies only when
+   * eavesdropping; eavesdrop=false means always deny.
+   */
+  if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop)
     {
-      BusPolicyRule *rule = link->data;
+      _dbus_verbose ("  (policy) skipping deny rule since it only applies to eavesdropping\n");
+      return FALSE;
+    }
 
-      link = _dbus_list_get_next_link (&policy->rules, link);      
-      
-      if (rule->type != BUS_POLICY_RULE_RECEIVE)
+  /* If it's a reply, the requested_reply flag kicks in */
+  if (dbus_message_get_reply_serial (message) != 0)
+    {
+      /* 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->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
         {
-          _dbus_verbose ("  (policy) skipping non-receive rule\n");
-          continue;
+          _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");
+          return FALSE;
         }
 
-      if (rule->d.receive.message_type != DBUS_MESSAGE_TYPE_INVALID)
+      /* for deny, requested_reply=false means the rule applies only
+       * when the reply was not requested. requested_reply=true means the
+       * rule always applies.
+       */
+      if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.requested_reply)
         {
-          if (dbus_message_get_type (message) != rule->d.receive.message_type)
-            {
-              _dbus_verbose ("  (policy) skipping rule for different message type\n");
-              continue;
-            }
+          _dbus_verbose ("  (policy) skipping deny rule since it only applies to unrequested replies\n");
+          return FALSE;
         }
+    }
 
-
-      /* for allow or check, eavesdrop=false means the rule doesn't apply when
-       * eavesdropping. eavesdrop=true means the rule always applies
-       */
-      if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop)
+  if (rule->d.receive.path != NULL)
+    {
+      if (dbus_message_get_path (message) != NULL &&
+          strcmp (dbus_message_get_path (message),
+                  rule->d.receive.path) != 0)
         {
-          _dbus_verbose ("  (policy) skipping %s rule since it doesn't apply to eavesdropping\n",
-              rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
-          continue;
+          _dbus_verbose ("  (policy) skipping rule for different path\n");
+          return FALSE;
         }
+    }
 
-      /* for deny, eavesdrop=true means the rule applies only when
-       * eavesdropping; eavesdrop=false means always deny.
+  if (rule->d.receive.interface != NULL)
+    {
+      /* 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). Check rules are treated like allow rules.
        */
-      if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop)
+      dbus_bool_t no_interface;
+
+      no_interface = dbus_message_get_interface (message) == NULL;
+
+      if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
+          (!no_interface &&
+           strcmp (dbus_message_get_interface (message),
+                   rule->d.receive.interface) != 0))
         {
-          _dbus_verbose ("  (policy) skipping deny rule since it only applies to eavesdropping\n");
-          continue;
+          _dbus_verbose ("  (policy) skipping rule for different interface\n");
+          return FALSE;
         }
+    }
 
-      /* If it's a reply, the requested_reply flag kicks in */
-      if (dbus_message_get_reply_serial (message) != 0)
+  if (rule->d.receive.member != NULL)
+    {
+      if (dbus_message_get_member (message) != NULL &&
+          strcmp (dbus_message_get_member (message),
+                  rule->d.receive.member) != 0)
         {
-          /* 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->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
-            {
-              _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;
-            }
+          _dbus_verbose ("  (policy) skipping rule for different member\n");
+          return FALSE;
+        }
+    }
 
-          /* for deny, requested_reply=false means the rule applies only
-           * when the reply was not requested. requested_reply=true means the
-           * rule always applies.
-           */
-          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;
-            }
+  if (rule->d.receive.error != NULL)
+    {
+      if (dbus_message_get_error_name (message) != NULL &&
+          strcmp (dbus_message_get_error_name (message),
+                  rule->d.receive.error) != 0)
+        {
+          _dbus_verbose ("  (policy) skipping rule for different error name\n");
+          return FALSE;
         }
-      
-      if (rule->d.receive.path != NULL)
+    }
+
+  if (rule->d.receive.origin != NULL)
+    {
+      /* sender can be NULL for messages that originate from the
+       * message bus itself, we check the strings in that case as
+       * built-in services don't have a DBusConnection but will
+       * still set the sender on their messages.
+       */
+      if (sender == NULL)
         {
-          if (dbus_message_get_path (message) != NULL &&
-              strcmp (dbus_message_get_path (message),
-                      rule->d.receive.path) != 0)
+          if (!dbus_message_has_sender (message,
+                                        rule->d.receive.origin))
             {
-              _dbus_verbose ("  (policy) skipping rule for different path\n");
-              continue;
+              _dbus_verbose ("  (policy) skipping rule because message sender is not %s\n",
+                             rule->d.receive.origin);
+              return FALSE;
             }
         }
-      
-      if (rule->d.receive.interface != NULL)
+      else
         {
-          /* 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). Check rules are treated like allow rules.
-           */
-          dbus_bool_t no_interface;
+          BusService *service;
+          DBusString str;
 
-          no_interface = dbus_message_get_interface (message) == NULL;
+          _dbus_string_init_const (&str, rule->d.receive.origin);
+
+          service = bus_registry_lookup (registry, &str);
           
-          if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
-              (!no_interface &&
-               strcmp (dbus_message_get_interface (message),
-                       rule->d.receive.interface) != 0))
+          if (service == NULL)
             {
-              _dbus_verbose ("  (policy) skipping rule for different interface\n");
-              continue;
+              _dbus_verbose ("  (policy) skipping rule because origin %s doesn't exist\n",
+                             rule->d.receive.origin);
+              return FALSE;
             }
-        }      
 
-      if (rule->d.receive.member != NULL)
-        {
-          if (dbus_message_get_member (message) != NULL &&
-              strcmp (dbus_message_get_member (message),
-                      rule->d.receive.member) != 0)
+          if (!bus_service_has_owner (service, sender))
             {
-              _dbus_verbose ("  (policy) skipping rule for different member\n");
-              continue;
+              _dbus_verbose ("  (policy) skipping rule because origin %s isn't owned by sender\n",
+                             rule->d.receive.origin);
+              return FALSE;
             }
         }
+    }
 
-      if (rule->d.receive.error != NULL)
+  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)
         {
-          if (dbus_message_get_error_name (message) != NULL &&
-              strcmp (dbus_message_get_error_name (message),
-                      rule->d.receive.error) != 0)
-            {
-              _dbus_verbose ("  (policy) skipping rule for different error name\n");
-              continue;
-            }
+          _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);
+          return FALSE;
         }
-      
-      if (rule->d.receive.origin != NULL)
-        {          
-          /* sender can be NULL for messages that originate from the
-           * message bus itself, we check the strings in that case as
-           * built-in services don't have a DBusConnection but will
-           * still set the sender on their messages.
-           */
-          if (sender == NULL)
-            {
-              if (!dbus_message_has_sender (message,
-                                            rule->d.receive.origin))
-                {
-                  _dbus_verbose ("  (policy) skipping rule because message sender is not %s\n",
-                                 rule->d.receive.origin);
-                  continue;
-                }
-            }
-          else
-            {
-              BusService *service;
-              DBusString str;
+    }
 
-              _dbus_string_init_const (&str, rule->d.receive.origin);
-              
-              service = bus_registry_lookup (registry, &str);
-              
-              if (service == NULL)
-                {
-                  _dbus_verbose ("  (policy) skipping rule because origin %s doesn't exist\n",
-                                 rule->d.receive.origin);
-                  continue;
-                }
+  /* Use this rule */
+  switch (rule->access)
+  {
+    case BUS_POLICY_RULE_ACCESS_ALLOW:
+      *result = BUS_RESULT_TRUE;
+      break;
+    case BUS_POLICY_RULE_ACCESS_DENY:
+      *result = BUS_RESULT_FALSE;
+      break;
+    case BUS_POLICY_RULE_ACCESS_CHECK:
+      *result = BUS_RESULT_LATER;
+      *privilege = rule->privilege;
+      break;
+  }
 
-              if (!bus_service_has_owner (service, sender))
-                {
-                  _dbus_verbose ("  (policy) skipping rule because origin %s isn't owned by sender\n",
-                                 rule->d.receive.origin);
-                  continue;
-                }
-            }
-        }
+  return TRUE;
+}
 
-      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);
+/* See docs on what the args mean on bus_context_check_security_policy()
+ * comment
+ */
+BusResult
+bus_client_policy_check_can_receive (BusClientPolicy     *policy,
+                                     BusRegistry         *registry,
+                                     dbus_bool_t          requested_reply,
+                                     DBusConnection      *sender,
+                                     DBusConnection      *addressed_recipient,
+                                     DBusConnection      *proposed_recipient,
+                                     DBusMessage         *message,
+                                     dbus_int32_t        *toggles,
+                                     const char         **privilege_param,
+                                     BusDeferredMessage **deferred_message,
+                                     char               **out_rule)
+{
+  DBusList *link;
+  dbus_bool_t eavesdropping;
+  BusResult result;
+  const char *privilege;
+  BusPolicyRule *matched_rule = NULL;
 
-          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;
-            }
-        }
+  eavesdropping =
+    addressed_recipient != proposed_recipient &&
+    dbus_message_get_destination (message) != NULL;
 
-      /* Use this rule */
-      switch (rule->access)
-      {
-        case BUS_POLICY_RULE_ACCESS_ALLOW:
-          result = BUS_RESULT_TRUE;
-          break;
-        case BUS_POLICY_RULE_ACCESS_DENY:
-          result = BUS_RESULT_FALSE;
-          break;
-        case BUS_POLICY_RULE_ACCESS_CHECK:
-          result = BUS_RESULT_LATER;
-          privilege = rule->privilege;
-          break;
-      }
+  /* policy->rules is in the order the rules appeared
+   * in the config file, i.e. last rule that applies wins
+   */
+
+  _dbus_verbose ("  (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
+  *toggles = 0;
 
-      (*toggles)++;
-      matched_rule = rule;
+  result = BUS_RESULT_FALSE;
+  link = _dbus_list_get_first_link (&policy->rules);
+  while (link != NULL)
+    {
+      const BusPolicyRule *rule = link->data;
 
-      _dbus_verbose ("  (policy) used rule, result now = %d\n",
-                     result);
+      link = _dbus_list_get_next_link (&policy->rules, link);
+
+      if (check_receive_rule (rule, registry, requested_reply, sender,
+                              message, eavesdropping, &result, &privilege))
+        {
+          (*toggles)++;
+          matched_rule = (BusPolicyRule *)rule;
+
+          _dbus_verbose ("  (policy) used rule, result now = %d\n",
+                         result);
+        }
     }
 
 
@@ -1430,7 +1462,50 @@ bus_client_policy_check_can_receive (BusClientPolicy     *policy,
   return result;
 }
 
+static dbus_bool_t
+check_own_rule (const BusPolicyRule *rule,
+                const DBusString    *service_name,
+                BusResult           *result,
+                const char         **privilege)
+{
+  /* Rule is skipped if it specifies a different service name from
+   * the desired one.
+   */
+
+  if (rule->type != BUS_POLICY_RULE_OWN)
+    return FALSE;
+
+  if (!rule->d.own.prefix && rule->d.own.service_name != NULL)
+    {
+      if (!_dbus_string_equal_c_str (service_name,
+                                     rule->d.own.service_name))
+        return FALSE;
+    }
+  else if (rule->d.own.prefix)
+    {
+      if (!_dbus_string_starts_with_words_c_str (service_name,
+                                                 rule->d.own.service_name,
+                                                 '.'))
+        return FALSE;
+    }
 
+  /* Use this rule */
+  switch (rule->access)
+  {
+  case BUS_POLICY_RULE_ACCESS_ALLOW:
+    *result = BUS_RESULT_TRUE;
+    break;
+  case BUS_POLICY_RULE_ACCESS_DENY:
+    *result = BUS_RESULT_FALSE;
+    break;
+  case BUS_POLICY_RULE_ACCESS_CHECK:
+    *result = BUS_RESULT_LATER;
+    *privilege = rule->privilege;
+    break;
+  }
+
+  return TRUE;
+}
 
 static BusResult
 bus_rules_check_can_own (DBusList *rules,
@@ -1450,45 +1525,11 @@ bus_rules_check_can_own (DBusList *rules,
   link = _dbus_list_get_first_link (&rules);
   while (link != NULL)
     {
-      BusPolicyRule *rule = link->data;
+      const BusPolicyRule *rule = link->data;
 
       link = _dbus_list_get_next_link (&rules, link);
       
-      /* Rule is skipped if it specifies a different service name from
-       * the desired one.
-       */
-      
-      if (rule->type != BUS_POLICY_RULE_OWN)
-        continue;
-
-      if (!rule->d.own.prefix && rule->d.own.service_name != NULL)
-        {
-          if (!_dbus_string_equal_c_str (service_name,
-                                         rule->d.own.service_name))
-            continue;
-        }
-      else if (rule->d.own.prefix)
-        {
-          if (!_dbus_string_starts_with_words_c_str (service_name,
-                                                     rule->d.own.service_name,
-                                                     '.'))
-            continue;
-        }
-
-      /* Use this rule */
-      switch (rule->access)
-      {
-      case BUS_POLICY_RULE_ACCESS_ALLOW:
-        result = BUS_RESULT_TRUE;
-        break;
-      case BUS_POLICY_RULE_ACCESS_DENY:
-        result = BUS_RESULT_FALSE;
-        break;
-      case BUS_POLICY_RULE_ACCESS_CHECK:
-        result = BUS_RESULT_LATER;
-        privilege = rule->privilege;
-        break;
-      }
+      check_own_rule (rule, service_name, &result, &privilege);
     }
 
   if (result == BUS_RESULT_LATER)