[daemon-dev][daemon-fix] starting services by direct message (autostart) and some...
[platform/upstream/dbus.git] / bus / policy.c
index 3de693f..082f385 100644 (file)
@@ -1,4 +1,4 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 /* policy.c  Bus security policy
  *
  * Copyright (C) 2003, 2004  Red Hat, Inc.
  * 
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 
+#include <config.h>
 #include "policy.h"
 #include "services.h"
 #include "test.h"
@@ -125,10 +126,12 @@ struct BusPolicy
 {
   int refcount;
 
-  DBusList *default_rules;       /**< Default policy rules */
-  DBusList *mandatory_rules;     /**< Mandatory policy rules */
-  DBusHashTable *rules_by_uid;   /**< per-UID policy rules */
-  DBusHashTable *rules_by_gid;   /**< per-GID policy rules */
+  DBusList *default_rules;         /**< Default policy rules */
+  DBusList *mandatory_rules;       /**< Mandatory policy rules */
+  DBusHashTable *rules_by_uid;     /**< per-UID policy rules */
+  DBusHashTable *rules_by_gid;     /**< per-GID policy rules */
+  DBusList *at_console_true_rules; /**< console user policy rules where at_console="true"*/
+  DBusList *at_console_false_rules; /**< console user policy rules where at_console="false"*/
 };
 
 static void
@@ -166,13 +169,13 @@ bus_policy_new (void)
 
   policy->refcount = 1;
   
-  policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
+  policy->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
                                                NULL,
                                                free_rule_list_func);
   if (policy->rules_by_uid == NULL)
     goto failed;
 
-  policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
+  policy->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
                                                NULL,
                                                free_rule_list_func);
   if (policy->rules_by_gid == NULL)
@@ -209,7 +212,13 @@ bus_policy_unref (BusPolicy *policy)
 
       _dbus_list_foreach (&policy->mandatory_rules, free_rule_func, NULL);
       _dbus_list_clear (&policy->mandatory_rules);
-      
+
+      _dbus_list_foreach (&policy->at_console_true_rules, free_rule_func, NULL);
+      _dbus_list_clear (&policy->at_console_true_rules);
+
+      _dbus_list_foreach (&policy->at_console_false_rules, free_rule_func, NULL);
+      _dbus_list_clear (&policy->at_console_false_rules);
+
       if (policy->rules_by_uid)
         {
           _dbus_hash_table_unref (policy->rules_by_uid);
@@ -264,7 +273,8 @@ bus_policy_create_client_policy (BusPolicy      *policy,
                                  DBusError      *error)
 {
   BusClientPolicy *client;
-  unsigned long uid;
+  dbus_uid_t uid;
+  dbus_bool_t at_console;
 
   _dbus_assert (dbus_connection_get_is_authenticated (connection));
   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -286,7 +296,7 @@ bus_policy_create_client_policy (BusPolicy      *policy,
       int n_groups;
       int i;
       
-      if (!bus_connection_get_groups (connection, &groups, &n_groups, error))
+      if (!bus_connection_get_unix_groups (connection, &groups, &n_groups, error))
         goto failed;
       
       i = 0;
@@ -294,7 +304,7 @@ bus_policy_create_client_policy (BusPolicy      *policy,
         {
           DBusList **list;
           
-          list = _dbus_hash_table_lookup_ulong (policy->rules_by_gid,
+          list = _dbus_hash_table_lookup_uintptr (policy->rules_by_gid,
                                                 groups[i]);
           
           if (list != NULL)
@@ -311,26 +321,39 @@ bus_policy_create_client_policy (BusPolicy      *policy,
 
       dbus_free (groups);
     }
-
-  if (!dbus_connection_get_unix_user (connection, &uid))
+  
+  if (dbus_connection_get_unix_user (connection, &uid))
     {
-      dbus_set_error (error, DBUS_ERROR_FAILED,
-                      "No user ID known for connection, cannot determine security policy\n");
-      goto failed;
-    }
+      if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
+        {
+          DBusList **list;
+          
+          list = _dbus_hash_table_lookup_uintptr (policy->rules_by_uid,
+                                                uid);
+          
+          if (list != NULL)
+            {
+              if (!add_list_to_client (list, client))
+                goto nomem;
+            }
+        }
 
-  if (_dbus_hash_table_get_n_entries (policy->rules_by_uid) > 0)
-    {
-      DBusList **list;
+      /* Add console rules */
+      at_console = _dbus_unix_user_is_at_console (uid, error);
       
-      list = _dbus_hash_table_lookup_ulong (policy->rules_by_uid,
-                                            uid);
-
-      if (list != NULL)
+      if (at_console)
         {
-          if (!add_list_to_client (list, client))
+          if (!add_list_to_client (&policy->at_console_true_rules, client))
             goto nomem;
         }
+      else if (dbus_error_is_set (error) == TRUE)
+        {
+          goto failed;
+        }
+      else if (!add_list_to_client (&policy->at_console_false_rules, client))
+        {
+          goto nomem;
+        }
     }
 
   if (!add_list_to_client (&policy->mandatory_rules,
@@ -367,7 +390,7 @@ list_allows_user (dbus_bool_t           def,
     {
       BusPolicyRule *rule = link->data;
       link = _dbus_list_get_next_link (list, link);
-      
+
       if (rule->type == BUS_POLICY_RULE_USER)
         {
           _dbus_verbose ("List %p user rule uid="DBUS_UID_FORMAT"\n",
@@ -380,8 +403,8 @@ list_allows_user (dbus_bool_t           def,
         }
       else if (rule->type == BUS_POLICY_RULE_GROUP)
         {
-          _dbus_verbose ("List %p group rule uid="DBUS_UID_FORMAT"\n",
-                         list, rule->d.user.uid);
+          _dbus_verbose ("List %p group rule gid="DBUS_GID_FORMAT"\n",
+                         list, rule->d.group.gid);
           
           if (rule->d.group.gid == DBUS_GID_UNSET)
             ;  /* '*' wildcard */
@@ -411,24 +434,23 @@ list_allows_user (dbus_bool_t           def,
 }
 
 dbus_bool_t
-bus_policy_allow_user (BusPolicy        *policy,
-                       DBusUserDatabase *user_database,
-                       unsigned long     uid)
+bus_policy_allow_unix_user (BusPolicy        *policy,
+                            unsigned long     uid)
 {
   dbus_bool_t allowed;
   unsigned long *group_ids;
   int n_group_ids;
 
   /* On OOM or error we always reject the user */
-  if (!_dbus_user_database_get_groups (user_database,
-                                       uid, &group_ids, &n_group_ids, NULL))
+  if (!_dbus_unix_groups_from_uid (uid, &group_ids, &n_group_ids))
     {
       _dbus_verbose ("Did not get any groups for UID %lu\n",
                      uid);
       return FALSE;
     }
-  
-  allowed = FALSE;
+
+  /* Default to "user owning bus" can connect */
+  allowed = _dbus_unix_user_is_process_owner (uid);
 
   allowed = list_allows_user (allowed,
                               &policy->default_rules,
@@ -447,6 +469,23 @@ bus_policy_allow_user (BusPolicy        *policy,
   return allowed;
 }
 
+/* For now this is never actually called because the default
+ * DBusConnection behavior of 'same user that owns the bus can
+ * connect' is all it would do. Set the windows user function in
+ * connection.c if the config file ever supports doing something
+ * interesting here.
+ */
+dbus_bool_t
+bus_policy_allow_windows_user (BusPolicy        *policy,
+                               const char       *windows_sid)
+{
+  /* Windows has no policies here since only the session bus
+   * is really used for now, so just checking that the
+   * connecting person is the same as the bus owner is fine.
+   */
+  return _dbus_windows_user_is_process_owner (windows_sid);
+}
+
 dbus_bool_t
 bus_policy_append_default_rule (BusPolicy      *policy,
                                 BusPolicyRule  *rule)
@@ -471,13 +510,15 @@ bus_policy_append_mandatory_rule (BusPolicy      *policy,
   return TRUE;
 }
 
+
+
 static DBusList**
 get_list (DBusHashTable *hash,
           unsigned long  key)
 {
   DBusList **list;
 
-  list = _dbus_hash_table_lookup_ulong (hash, key);
+  list = _dbus_hash_table_lookup_uintptr (hash, key);
 
   if (list == NULL)
     {
@@ -485,7 +526,7 @@ get_list (DBusHashTable *hash,
       if (list == NULL)
         return NULL;
 
-      if (!_dbus_hash_table_insert_ulong (hash, key, list))
+      if (!_dbus_hash_table_insert_uintptr (hash, key, list))
         {
           dbus_free (list);
           return NULL;
@@ -535,6 +576,28 @@ bus_policy_append_group_rule (BusPolicy      *policy,
   return TRUE;
 }
 
+dbus_bool_t
+bus_policy_append_console_rule (BusPolicy      *policy,
+                                dbus_bool_t     at_console,
+                                BusPolicyRule  *rule)
+{
+  if (at_console)
+    {
+      if (!_dbus_list_append (&policy->at_console_true_rules, rule))
+        return FALSE;
+    }
+    else
+    {
+      if (!_dbus_list_append (&policy->at_console_false_rules, rule))
+        return FALSE;
+    }
+
+  bus_policy_rule_ref (rule);
+
+  return TRUE;
+
+}
+
 static dbus_bool_t
 append_copy_of_policy_list (DBusList **list,
                             DBusList **to_append)
@@ -576,7 +639,7 @@ merge_id_hash (DBusHashTable *dest,
   _dbus_hash_iter_init (to_absorb, &iter);
   while (_dbus_hash_iter_next (&iter))
     {
-      unsigned long id = _dbus_hash_iter_get_ulong_key (&iter);
+      unsigned long id = _dbus_hash_iter_get_uintptr_key (&iter);
       DBusList **list = _dbus_hash_iter_get_value (&iter);
       DBusList **target = get_list (dest, id);
 
@@ -606,6 +669,14 @@ bus_policy_merge (BusPolicy *policy,
                                    &to_absorb->mandatory_rules))
     return FALSE;
 
+  if (!append_copy_of_policy_list (&policy->at_console_true_rules,
+                                   &to_absorb->at_console_true_rules))
+    return FALSE;
+
+  if (!append_copy_of_policy_list (&policy->at_console_false_rules,
+                                   &to_absorb->at_console_false_rules))
+    return FALSE;
+
   if (!merge_id_hash (policy->rules_by_uid,
                       to_absorb->rules_by_uid))
     return FALSE;
@@ -796,7 +867,9 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
                                   BusRegistry     *registry,
                                   dbus_bool_t      requested_reply,
                                   DBusConnection  *receiver,
-                                  DBusMessage     *message)
+                                  DBusMessage     *message,
+                                  dbus_int32_t    *toggles,
+                                  dbus_bool_t     *log)
 {
   DBusList *link;
   dbus_bool_t allowed;
@@ -806,6 +879,7 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
    */
 
   _dbus_verbose ("  (policy) checking send rules\n");
+  *toggles = 0;
   
   allowed = FALSE;
   link = _dbus_list_get_first_link (&policy->rules);
@@ -842,9 +916,9 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
            * only when reply was requested. requested_reply=false means
            * always allow.
            */
-          if (!requested_reply && rule->allow && rule->d.send.requested_reply)
+          if (!requested_reply && rule->allow && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
             {
-              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies\n");
+              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
               continue;
             }
 
@@ -872,9 +946,19 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
       
       if (rule->d.send.interface != NULL)
         {
-          if (dbus_message_get_interface (message) != NULL &&
-              strcmp (dbus_message_get_interface (message),
-                      rule->d.send.interface) != 0)
+          /* 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).
+           */
+          dbus_bool_t no_interface;
+
+          no_interface = dbus_message_get_interface (message) == NULL;
+          
+          if ((no_interface && rule->allow) ||
+              (!no_interface && 
+               strcmp (dbus_message_get_interface (message),
+                       rule->d.send.interface) != 0))
             {
               _dbus_verbose ("  (policy) skipping rule for different interface\n");
               continue;
@@ -946,6 +1030,8 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
 
       /* Use this rule */
       allowed = rule->allow;
+      *log = rule->d.send.log;
+      (*toggles)++;
 
       _dbus_verbose ("  (policy) used rule, allow now = %d\n",
                      allowed);
@@ -964,7 +1050,8 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
                                      DBusConnection  *sender,
                                      DBusConnection  *addressed_recipient,
                                      DBusConnection  *proposed_recipient,
-                                     DBusMessage     *message)
+                                     DBusMessage     *message,
+                                     dbus_int32_t    *toggles)
 {
   DBusList *link;
   dbus_bool_t allowed;
@@ -979,6 +1066,7 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
    */
 
   _dbus_verbose ("  (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
+  *toggles = 0;
   
   allowed = FALSE;
   link = _dbus_list_get_first_link (&policy->rules);
@@ -1028,9 +1116,9 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
            * only when reply was requested. requested_reply=false means
            * always allow.
            */
-          if (!requested_reply && rule->allow && rule->d.receive.requested_reply)
+          if (!requested_reply && rule->allow && rule->d.receive.requested_reply && !rule->d.receive.eavesdrop)
             {
-              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies\n");
+              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
               continue;
             }
 
@@ -1058,9 +1146,19 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
       
       if (rule->d.receive.interface != NULL)
         {
-          if (dbus_message_get_interface (message) != NULL &&
-              strcmp (dbus_message_get_interface (message),
-                      rule->d.receive.interface) != 0)
+          /* 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).
+           */
+          dbus_bool_t no_interface;
+
+          no_interface = dbus_message_get_interface (message) == NULL;
+          
+          if ((no_interface && rule->allow) ||
+              (!no_interface &&
+               strcmp (dbus_message_get_interface (message),
+                       rule->d.receive.interface) != 0))
             {
               _dbus_verbose ("  (policy) skipping rule for different interface\n");
               continue;
@@ -1133,6 +1231,7 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
       
       /* Use this rule */
       allowed = rule->allow;
+      (*toggles)++;
 
       _dbus_verbose ("  (policy) used rule, allow now = %d\n",
                      allowed);
@@ -1141,25 +1240,26 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
   return allowed;
 }
 
-dbus_bool_t
-bus_client_policy_check_can_own (BusClientPolicy  *policy,
-                                 DBusConnection   *connection,
-                                 const DBusString *service_name)
+
+
+static dbus_bool_t
+bus_rules_check_can_own (DBusList *rules,
+                         const DBusString *service_name)
 {
   DBusList *link;
   dbus_bool_t allowed;
   
-  /* policy->rules is in the order the rules appeared
+  /* 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_link (&policy->rules);
+  link = _dbus_list_get_first_link (&rules);
   while (link != NULL)
     {
       BusPolicyRule *rule = link->data;
 
-      link = _dbus_list_get_next_link (&policy->rules, link);
+      link = _dbus_list_get_next_link (&rules, link);
       
       /* Rule is skipped if it specifies a different service name from
        * the desired one.
@@ -1168,12 +1268,25 @@ bus_client_policy_check_can_own (BusClientPolicy  *policy,
       if (rule->type != BUS_POLICY_RULE_OWN)
         continue;
 
-      if (rule->d.own.service_name != NULL)
+      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)
+        {
+          const char *data;
+          char next_char;
+          if (!_dbus_string_starts_with_c_str (service_name,
+                                               rule->d.own.service_name))
+            continue;
+
+          data = _dbus_string_get_const_data (service_name);
+          next_char = data[strlen (rule->d.own.service_name)];
+          if (next_char != '\0' && next_char != '.')
+            continue;
+        }
 
       /* Use this rule */
       allowed = rule->allow;
@@ -1182,17 +1295,19 @@ bus_client_policy_check_can_own (BusClientPolicy  *policy,
   return allowed;
 }
 
-#ifdef DBUS_BUILD_TESTS
+dbus_bool_t
+bus_client_policy_check_can_own (BusClientPolicy  *policy,
+                                 const DBusString *service_name)
+{
+  return bus_rules_check_can_own (policy->rules, service_name);
+}
 
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
 dbus_bool_t
-bus_policy_test (const DBusString *test_data_dir)
+bus_policy_check_can_own (BusPolicy  *policy,
+                          const DBusString *service_name)
 {
-  /* This doesn't do anything for now because I decided to do it in
-   * dispatch.c instead by having some of the clients in dispatch.c
-   * have particular policies applied to them.
-   */
-  
-  return TRUE;
+  return bus_rules_check_can_own (policy->default_rules, service_name);
 }
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */
 
-#endif /* DBUS_BUILD_TESTS */