2003-03-20 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Thu, 20 Mar 2003 07:57:39 +0000 (07:57 +0000)
committerHavoc Pennington <hp@redhat.com>
Thu, 20 Mar 2003 07:57:39 +0000 (07:57 +0000)
* bus/connection.c (bus_connection_send_oom_error): assert that
message has a sender
(connection_execute_transaction): ditto
(bus_connection_preallocate_oom_error): fix to set the sender, and
set recipient to the destination service, not the bus driver

* bus/policy.c: hacking

* dbus/dbus-message.c (dbus_message_service_is): new function
(dbus_message_sender_is): new

ChangeLog
bus/Makefile.am
bus/connection.c
bus/policy.c
bus/policy.h
dbus/dbus-message.c
dbus/dbus-message.h

index 4c6fe3e..4688cdb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2003-03-20  Havoc Pennington  <hp@pobox.com>
+
+       * bus/connection.c (bus_connection_send_oom_error): assert that
+       message has a sender
+       (connection_execute_transaction): ditto
+       (bus_connection_preallocate_oom_error): fix to set the sender, and
+       set recipient to the destination service, not the bus driver
+
+       * bus/policy.c: hacking
+
+       * dbus/dbus-message.c (dbus_message_service_is): new function
+       (dbus_message_sender_is): new
+
 2003-03-19  Havoc Pennington  <hp@redhat.com>
 
        * bus/policy.c: start sketching code for policy restrictions on 
index aee6e37..bc64032 100644 (file)
@@ -21,6 +21,8 @@ BUS_SOURCES=                                  \
        driver.h                                \
        loop.c                                  \
        loop.h                                  \
+       policy.c                                \
+       policy.h                                \
        services.c                              \
        services.h                              \
        test.c                                  \
index 1b59819..b01cbde 100644 (file)
@@ -504,7 +504,8 @@ bus_connection_preallocate_oom_error (DBusConnection *connection)
   if (preallocated == NULL)
     return FALSE;
 
-  message = dbus_message_new (DBUS_SERVICE_DBUS,
+  /* d->name may be NULL, but that should be OK */
+  message = dbus_message_new (d->name,
                               DBUS_ERROR_NO_MEMORY);
   if (message == NULL)
     {
@@ -513,6 +514,14 @@ bus_connection_preallocate_oom_error (DBusConnection *connection)
     }
 
   dbus_message_set_is_error (message, TRUE);
+
+  if (!dbus_message_set_sender (message,
+                                DBUS_SERVICE_DBUS))
+    {
+      dbus_connection_free_preallocated_send (connection, preallocated);
+      dbus_message_unref (message);
+      return FALSE;
+    }
   
   /* set reply serial to placeholder value just so space is already allocated
    * for it.
@@ -546,6 +555,8 @@ bus_connection_send_oom_error (DBusConnection *connection,
                                       dbus_message_get_serial (in_reply_to)))
     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
 
+  _dbus_assert (dbus_message_get_sender (d->oom_message) != NULL);
+  
   dbus_connection_send_preallocated (connection, d->oom_preallocated,
                                      d->oom_message, NULL);
 
@@ -681,6 +692,8 @@ bus_transaction_send_message (BusTransaction *transaction,
                  dbus_message_get_name (message),
                  dbus_connection_get_is_connected (connection) ?
                  "" : " (disconnected)");
+
+  _dbus_assert (dbus_message_get_sender (message) != NULL);
   
   if (!dbus_connection_get_is_connected (connection))
     return TRUE; /* silently ignore disconnected connections */
@@ -811,6 +824,8 @@ connection_execute_transaction (DBusConnection *connection,
           _dbus_list_remove_link (&d->transaction_messages,
                                   link);
 
+          _dbus_assert (dbus_message_get_sender (m->message) != NULL);
+          
           dbus_connection_send_preallocated (connection,
                                              m->preallocated,
                                              m->message,
index c94c17e..f916383 100644 (file)
@@ -22,6 +22,9 @@
  */
 
 #include "policy.h"
+#include "services.h"
+#include <dbus/dbus-list.h>
+#include <dbus/dbus-internals.h>
 
 BusPolicyRule*
 bus_policy_rule_new (BusPolicyRuleType type,
@@ -59,15 +62,15 @@ bus_policy_rule_unref (BusPolicyRule *rule)
     {
       switch (rule->type)
         {
-        case DBUS_POLICY_RULE_SEND:
+        case BUS_POLICY_RULE_SEND:
           dbus_free (rule->d.send.message_name);
           dbus_free (rule->d.send.destination);
           break;
-        case DBUS_POLICY_RULE_RECEIVE:
+        case BUS_POLICY_RULE_RECEIVE:
           dbus_free (rule->d.receive.message_name);
           dbus_free (rule->d.receive.origin);
           break;
-        case DBUS_POLICY_RULE_OWN:
+        case BUS_POLICY_RULE_OWN:
           dbus_free (rule->d.own.service_name);
           break;
         }
@@ -133,31 +136,262 @@ bus_policy_unref (BusPolicy *policy)
     }
 }
 
+static void
+remove_rules_by_type_up_to (BusPolicy         *policy,
+                            BusPolicyRuleType  type,
+                            DBusList          *up_to)
+{
+  DBusList *link;
+
+  link = _dbus_list_get_first (&policy->rules);
+  while (link != up_to)
+    {
+      BusPolicyRule *rule = link->data;
+      DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
+
+      bus_policy_rule_unref (rule);
+      _dbus_list_remove_link (&policy->rules, link);
+
+      link = next;
+    }
+}
+
+static void
+bus_policy_optimize (BusPolicy *policy)
+{
+  DBusList *link;
+
+  /* The idea here is that if we have:
+   * 
+   * <allow send="foo"/>
+   * <deny send="*"/>
+   *
+   * (for example) the deny will always override the allow.  So we
+   * delete the allow. Ditto for deny followed by allow, etc. This is
+   * a dumb thing to put in a config file, but the <include> feature
+   * of files allows for an "inheritance and override" pattern where
+   * it could make sense. If an included file wants to "start over"
+   * with a blanket deny, no point keeping the rules from the parent
+   * file.
+   */
+
+  link = _dbus_list_get_first (&policy->rules);
+  while (link != NULL)
+    {
+      BusPolicyRule *rule = link->data;
+      DBusList *next = _dbus_list_get_next_link (&policy->rules, link);
+      dbus_bool_t remove_preceding;
+
+      remove_preceding = FALSE;
+      
+      switch (rule->type)
+        {
+        case BUS_POLICY_RULE_SEND:
+          remove_preceding =
+            rule->d.send.message_name == NULL &&
+            rule->d.send.destination == NULL;
+          break;
+        case BUS_POLICY_RULE_RECEIVE:
+          remove_preceding =
+            rule->d.receive.message_name == NULL &&
+            rule->d.receive.origin == NULL;
+          break;
+        case BUS_POLICY_RULE_OWN:
+          remove_preceding =
+            rule->d.own.service_name == NULL;
+          break;
+        }
+                
+      if (remove_preceding)
+        remove_rules_by_type_up_to (policy, rule->type,
+                                    link);
+      
+      link = next;
+    }
+}
+
 dbus_bool_t
 bus_policy_check_can_send (BusPolicy      *policy,
-                           DBusConnection *sender,
+                           BusRegistry    *registry,
+                           DBusConnection *receiver,
                            DBusMessage    *message)
 {
+  DBusList *link;
+  dbus_bool_t allowed;
   
+  /* policy->rules is in the order the rules appeared
+   * in the config file, i.e. last rule that applies wins
+   */
+
+  allowed = FALSE;
+  link = _dbus_list_get_first (&policy->rules);
+  while (link != NULL)
+    {
+      BusPolicyRule *rule = link->data;
+
+      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 (rule->type != BUS_POLICY_RULE_SEND)
+        continue;
+
+      if (rule->d.send.message_name != NULL)
+        {
+          if (!dbus_message_name_is (message,
+                                     rule->d.send.message_name))
+            continue;
+        }
+
+      if (rule->d.send.destination != NULL)
+        {
+          /* 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.
+           */
+          if (receiver == NULL)
+            {
+              if (!dbus_message_sender_is (message,
+                                           rule->d.send.destination))
+                continue;
+            }
+          else
+            {
+              DBusString str;
+              BusService *service;
+              
+              _dbus_string_init_const (&str, rule->d.send.destination);
+              
+              service = bus_registry_lookup (registry, &str);
+              if (service == NULL)
+                continue;
+
+              if (!bus_service_has_owner (service, receiver))
+                continue;
+            }
+        }
+
+      /* Use this rule */
+      allowed = rule->allow;
+    }
 
+  return allowed;
 }
 
 dbus_bool_t
 bus_policy_check_can_receive (BusPolicy      *policy,
-                              DBusConnection *receiver,
+                              BusRegistry    *registry,
+                              DBusConnection *sender,
                               DBusMessage    *message)
 {
+  DBusList *link;
+  dbus_bool_t allowed;
+  
+  /* policy->rules is in the order the rules appeared
+   * in the config file, i.e. last rule that applies wins
+   */
+
+  allowed = FALSE;
+  link = _dbus_list_get_first (&policy->rules);
+  while (link != NULL)
+    {
+      BusPolicyRule *rule = link->data;
+
+      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
+       * origin from the message
+       */
+      
+      if (rule->type != BUS_POLICY_RULE_RECEIVE)
+        continue;
+
+      if (rule->d.receive.message_name != NULL)
+        {
+          if (!dbus_message_name_is (message,
+                                     rule->d.receive.message_name))
+            continue;
+        }
+
+      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_sender_is (message,
+                                           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)
+                continue;
+
+              if (!bus_service_has_owner (service, sender))
+                continue;
+            }
+        }
 
+      /* Use this rule */
+      allowed = rule->allow;
+    }
 
+  return allowed;
 }
 
 dbus_bool_t
-bus_policy_check_can_own (BusPolicy      *policy,
-                          DBusConnection *connection,
-                          const char     *service_name)
+bus_policy_check_can_own (BusPolicy        *policy,
+                          DBusConnection   *connection,
+                          const DBusString *service_name)
 {
+  DBusList *link;
+  dbus_bool_t allowed;
+  
+  /* policy->rules is in the order the rules appeared
+   * in the config file, i.e. last rule that applies wins
+   */
 
+  allowed = FALSE;
+  link = _dbus_list_get_first (&policy->rules);
+  while (link != NULL)
+    {
+      BusPolicyRule *rule = link->data;
 
-}
+      link = _dbus_list_get_next_link (&policy->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.service_name != NULL)
+        {
+          if (!_dbus_string_equal_c_str (service_name,
+                                         rule->d.own.service_name))
+            continue;
+        }
 
-#endif /* BUS_POLICY_H */
+      /* Use this rule */
+      allowed = rule->allow;
+    }
+
+  return allowed;
+}
index f146c52..00d60ba 100644 (file)
@@ -25,6 +25,7 @@
 #define BUS_POLICY_H
 
 #include <dbus/dbus.h>
+#include <dbus/dbus-string.h>
 #include "bus.h"
 
 typedef struct BusPolicy     BusPolicy;
@@ -32,9 +33,9 @@ typedef struct BusPolicyRule BusPolicyRule;
 
 typedef enum
 {
-  DBUS_POLICY_RULE_SEND,
-  DBUS_POLICY_RULE_RECEIVE,
-  DBUS_POLICY_RULE_OWN
+  BUS_POLICY_RULE_SEND,
+  BUS_POLICY_RULE_RECEIVE,
+  BUS_POLICY_RULE_OWN
 } BusPolicyRuleType;
 
 struct BusPolicyRule
@@ -76,17 +77,19 @@ void           bus_policy_rule_ref   (BusPolicyRule    *rule);
 void           bus_policy_rule_unref (BusPolicyRule    *rule);
 
 BusPolicy*  bus_policy_new               (void);
-void        bus_policy_ref               (BusPolicy      *policy);
-void        bus_policy_unref             (BusPolicy      *policy);
-dbus_bool_t bus_policy_check_can_send    (BusPolicy      *policy,
-                                          DBusConnection *sender,
-                                          DBusMessage    *message);
-dbus_bool_t bus_policy_check_can_receive (BusPolicy      *policy,
-                                          DBusConnection *receiver,
-                                          DBusMessage    *message);
-dbus_bool_t bus_policy_check_can_own     (BusPolicy      *policy,
-                                          DBusConnection *connection,
-                                          const char     *service_name);
+void        bus_policy_ref               (BusPolicy        *policy);
+void        bus_policy_unref             (BusPolicy        *policy);
+dbus_bool_t bus_policy_check_can_send    (BusPolicy        *policy,
+                                          BusRegistry      *registry,
+                                          DBusConnection   *receiver,
+                                          DBusMessage      *message);
+dbus_bool_t bus_policy_check_can_receive (BusPolicy        *policy,
+                                          BusRegistry      *registry,
+                                          DBusConnection   *sender,
+                                          DBusMessage      *message);
+dbus_bool_t bus_policy_check_can_own     (BusPolicy        *policy,
+                                          DBusConnection   *connection,
+                                          const DBusString *service_name);
 
 
 
index defa058..6366c54 100644 (file)
@@ -1018,6 +1018,11 @@ dbus_message_get_name (DBusMessage *message)
 /**
  * Gets the destination service of a message.
  *
+ * @todo I think if we have set_sender/get_sender,
+ * this function might be better named set_destination/
+ * get_destination for clarity, as the sender
+ * is also a service name.
+ * 
  * @param message the message
  * @returns the message destination service (should not be freed)
  */
@@ -2326,6 +2331,62 @@ dbus_message_name_is (DBusMessage *message,
     return FALSE;
 }
 
+/**
+ * Checks whether the message was sent to the given service.  If the
+ * message has no service specified or has a different name, returns
+ * #FALSE.
+ *
+ * @param message the message
+ * @param service the service to check (must not be #NULL)
+ * 
+ * @returns #TRUE if the message has the given destination service
+ */
+dbus_bool_t
+dbus_message_service_is (DBusMessage  *message,
+                         const char   *service)
+{
+  const char *s;
+
+  _dbus_assert (service != NULL);
+  
+  s = dbus_message_get_service (message);
+
+  if (s && strcmp (s, service) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/**
+ * Checks whether the message has the given service as its sender.  If
+ * the message has no sender specified or has a different sender,
+ * returns #FALSE. Note that if a peer application owns multiple
+ * services, its messages will have only one of those services as the
+ * sender (usually the base service). So you can't use this
+ * function to prove the sender didn't own service Foo, you can
+ * only use it to prove that it did.
+ *
+ * @param message the message
+ * @param service the service to check (must not be #NULL)
+ * 
+ * @returns #TRUE if the message has the given origin service
+ */
+dbus_bool_t
+dbus_message_sender_is (DBusMessage  *message,
+                        const char   *service)
+{
+  const char *s;
+
+  _dbus_assert (service != NULL);
+  
+  s = dbus_message_get_sender (message);
+
+  if (s && strcmp (s, service) == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
 /** @} */
 
 /**
index 1d5bbeb..be752c9 100644 (file)
@@ -58,6 +58,10 @@ void         dbus_message_set_is_error     (DBusMessage  *message,
 dbus_bool_t  dbus_message_get_is_error     (DBusMessage  *message);
 dbus_bool_t  dbus_message_name_is          (DBusMessage  *message,
                                             const char   *name);
+dbus_bool_t  dbus_message_service_is       (DBusMessage  *message,
+                                            const char   *service);
+dbus_bool_t  dbus_message_sender_is        (DBusMessage  *message,
+                                            const char   *service);
 dbus_int32_t dbus_message_get_serial       (DBusMessage  *message);
 dbus_bool_t  dbus_message_set_reply_serial (DBusMessage  *message,
                                             dbus_int32_t  reply_serial);