2003-04-27 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / bus.c
index 3b2ffc0..e0f6648 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -43,15 +43,9 @@ struct BusContext
   BusConnections *connections;
   BusActivation *activation;
   BusRegistry *registry;
-  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 */
-  int activation_timeout;        /**< How long to wait for an activation to time out */
-  int auth_timeout;              /**< How long to wait for an authentication to time out */
-  int max_completed_connections;    /**< Max number of authorized connections */
-  int max_incomplete_connections;   /**< Max number of incomplete connections */
-  int max_connections_per_user;     /**< Max number of connections auth'd as same user */
+  BusPolicy *policy;
+  DBusUserDatabase *user_database;
+  BusLimits limits;
 };
 
 static int server_data_slot = -1;
@@ -124,9 +118,12 @@ server_watch_callback (DBusWatch     *watch,
                        unsigned int   condition,
                        void          *data)
 {
-  DBusServer *server = data;
-
-  return dbus_server_handle_watch (server, watch, condition);
+  /* FIXME this can be done in dbus-mainloop.c
+   * if the code in activation.c for the babysitter
+   * watch handler is fixed.
+   */
+  
+  return dbus_watch_handle (watch, condition);
 }
 
 static dbus_bool_t
@@ -209,29 +206,14 @@ new_connection_callback (DBusServer     *server,
        */
       dbus_connection_disconnect (new_connection);
     }
-  
-  /* on OOM, we won't have ref'd the connection so it will die. */
-}
 
-static void
-free_rule_func (void *data,
-                void *user_data)
-{
-  BusPolicyRule *rule = data;
+  dbus_connection_set_max_received_size (new_connection,
+                                         context->limits.max_incoming_bytes);
 
-  bus_policy_rule_unref (rule);
-}
-
-static void
-free_rule_list_func (void *data)
-{
-  DBusList **list = data;
-
-  _dbus_list_foreach (list, free_rule_func, NULL);
+  dbus_connection_set_max_message_size (new_connection,
+                                        context->limits.max_message_size);
   
-  _dbus_list_clear (list);
-
-  dbus_free (list);
+  /* on OOM, we won't have ref'd the connection so it will die. */
 }
 
 static void
@@ -368,33 +350,21 @@ bus_context_new (const DBusString *config_file,
   
   context->refcount = 1;
 
+  /* get our limits and timeout lengths */
+  bus_config_parser_get_limits (parser, &context->limits);
+  
   /* we need another ref of the server data slot for the context
    * to own
    */
   if (!server_data_slot_ref ())
     _dbus_assert_not_reached ("second ref of server data slot failed");
   
-#ifdef DBUS_BUILD_TESTS
-  context->activation_timeout = 6000;  /* 6 seconds */
-#else
-  context->activation_timeout = 15000; /* 15 seconds */
-#endif
-
-  /* Making this long risks making a DOS attack easier, but too short
-   * and legitimate auth will fail.  If interactive auth (ask user for
-   * password) is allowed, then potentially it has to be quite long.
-   * Ultimately it needs to come from the configuration file.
-   */     
-  context->auth_timeout = 3000; /* 3 seconds */
-
-  context->max_incomplete_connections = 32;
-  context->max_connections_per_user = 128;
-
-  /* Note that max_completed_connections / max_connections_per_user
-   * is the number of users that would have to work together to
-   * DOS all the other users.
-   */
-  context->max_completed_connections = 1024;
+  context->user_database = _dbus_user_database_new ();
+  if (context->user_database == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
   
   context->loop = _dbus_loop_new ();
   if (context->loop == NULL)
@@ -565,25 +535,10 @@ bus_context_new (const DBusString *config_file,
       BUS_SET_OOM (error);
       goto failed;
     }
-  
-  context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
-                                                NULL,
-                                                free_rule_list_func);
-  if (context->rules_by_uid == NULL)
-    {
-      BUS_SET_OOM (error);
-      goto failed;
-    }
-
-  context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
-                                                NULL,
-                                                free_rule_list_func);
-  if (context->rules_by_gid == NULL)
-    {
-      BUS_SET_OOM (error);
-      goto failed;
-    }
 
+  context->policy = bus_config_parser_steal_policy (parser);
+  _dbus_assert (context->policy != NULL);
+  
   /* Now become a daemon if appropriate */
   if (bus_config_parser_get_fork (parser))
     {
@@ -744,18 +699,12 @@ bus_context_unref (BusContext *context)
         }
       _dbus_list_clear (&context->servers);
 
-      if (context->rules_by_uid)
-        {
-          _dbus_hash_table_unref (context->rules_by_uid);
-          context->rules_by_uid = NULL;
-        }
-
-      if (context->rules_by_gid)
+      if (context->policy)
         {
-          _dbus_hash_table_unref (context->rules_by_gid);
-          context->rules_by_gid = NULL;
+          bus_policy_unref (context->policy);
+          context->policy = NULL;
         }
-
+      
       if (context->loop)
         {
           _dbus_loop_unref (context->loop);
@@ -778,6 +727,8 @@ bus_context_unref (BusContext *context)
           dbus_free (context->pidfile); 
        }
 
+      _dbus_user_database_unref (context->user_database);
+      
       dbus_free (context);
 
       server_data_slot_unref ();
@@ -821,189 +772,196 @@ bus_context_get_loop (BusContext *context)
   return context->loop;
 }
 
-static dbus_bool_t
-list_allows_user (dbus_bool_t           def,
-                  DBusList            **list,
-                  unsigned long         uid,
-                  const unsigned long  *group_ids,
-                  int                   n_group_ids)
+DBusUserDatabase*
+bus_context_get_user_database (BusContext *context)
 {
-  DBusList *link;
-  dbus_bool_t allowed;
-  
-  allowed = def;
-
-  link = _dbus_list_get_first_link (list);
-  while (link != NULL)
-    {
-      BusPolicyRule *rule = link->data;
-      link = _dbus_list_get_next_link (list, link);
-      
-      if (rule->type == BUS_POLICY_RULE_USER)
-        {
-          if (rule->d.user.uid != uid)
-            continue;
-        }
-      else if (rule->type == BUS_POLICY_RULE_GROUP)
-        {
-          int i;
-
-          i = 0;
-          while (i < n_group_ids)
-            {
-              if (rule->d.group.gid == group_ids[i])
-                break;
-              ++i;
-            }
-
-          if (i == n_group_ids)
-            continue;
-        }
-      else
-        continue;
-
-      allowed = rule->allow;
-    }
-  
-  return allowed;
+  return context->user_database;
 }
 
 dbus_bool_t
 bus_context_allow_user (BusContext   *context,
                         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_get_groups (uid, &group_ids, &n_group_ids))
-    {
-      _dbus_verbose ("Did not get any groups for UID %lu\n",
-                     uid);
-      return FALSE;
-    }
-  
-  allowed = FALSE;
+  return bus_policy_allow_user (context->policy,
+                                context->user_database,
+                                uid);
+}
 
-  allowed = list_allows_user (allowed,
-                              &context->default_rules,
-                              uid,
-                              group_ids, n_group_ids);
+BusClientPolicy*
+bus_context_create_client_policy (BusContext      *context,
+                                  DBusConnection  *connection,
+                                  DBusError       *error)
+{
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  return bus_policy_create_client_policy (context->policy, connection,
+                                          error);
+}
 
-  allowed = list_allows_user (allowed,
-                              &context->mandatory_rules,
-                              uid,
-                              group_ids, n_group_ids);
+int
+bus_context_get_activation_timeout (BusContext *context)
+{
+  
+  return context->limits.activation_timeout;
+}
 
-  dbus_free (group_ids);
+int
+bus_context_get_auth_timeout (BusContext *context)
+{
+  return context->limits.auth_timeout;
+}
 
-  return allowed;
+int
+bus_context_get_max_completed_connections (BusContext *context)
+{
+  return context->limits.max_completed_connections;
 }
 
-static dbus_bool_t
-add_list_to_policy (DBusList       **list,
-                    BusPolicy       *policy)
+int
+bus_context_get_max_incomplete_connections (BusContext *context)
 {
-  DBusList *link;
+  return context->limits.max_incomplete_connections;
+}
 
-  link = _dbus_list_get_first_link (list);
-  while (link != NULL)
-    {
-      BusPolicyRule *rule = link->data;
-      link = _dbus_list_get_next_link (list, link);
+int
+bus_context_get_max_connections_per_user (BusContext *context)
+{
+  return context->limits.max_connections_per_user;
+}
 
-      switch (rule->type)
-        {
-        case BUS_POLICY_RULE_USER:
-        case BUS_POLICY_RULE_GROUP:
-          /* These aren't per-connection policies */
-          break;
-
-        case BUS_POLICY_RULE_OWN:
-        case BUS_POLICY_RULE_SEND:
-        case BUS_POLICY_RULE_RECEIVE:
-          /* These are per-connection */
-          if (!bus_policy_append_rule (policy, rule))
-            return FALSE;
-          break;
-        }
-    }
-  
-  return TRUE;
+int
+bus_context_get_max_pending_activations (BusContext *context)
+{
+  return context->limits.max_pending_activations;
 }
 
-BusPolicy*
-bus_context_create_connection_policy (BusContext      *context,
-                                      DBusConnection  *connection)
+int
+bus_context_get_max_services_per_connection (BusContext *context)
 {
-  BusPolicy *policy;
-  unsigned long uid;
-  DBusList **list;
+  return context->limits.max_services_per_connection;
+}
 
-  _dbus_assert (dbus_connection_get_is_authenticated (connection));
-  
-  policy = bus_policy_new ();
-  if (policy == NULL)
-    return NULL;
+dbus_bool_t
+bus_context_check_security_policy (BusContext     *context,
+                                   DBusConnection *sender,
+                                   DBusConnection *recipient,
+                                   DBusMessage    *message,
+                                   DBusError      *error)
+{
+  BusClientPolicy *sender_policy;
+  BusClientPolicy *recipient_policy;
 
-  if (!add_list_to_policy (&context->default_rules,
-                                      policy))
-    goto failed;
+  /* NULL sender/receiver means the bus driver */
 
-  /* we avoid the overhead of looking up user's groups
-   * if we don't have any group rules anyway
-   */
-  if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0)
+  if (sender != NULL)
     {
-      const unsigned long *groups;
-      int n_groups;
-      int i;
-      
-      if (!bus_connection_get_groups (connection, &groups, &n_groups))
-        goto failed;
-      
-      i = 0;
-      while (i < n_groups)
+      if (bus_connection_is_active (sender))
         {
-          list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
-                                                groups[i]);
-          
-          if (list != NULL)
+          sender_policy = bus_connection_get_policy (sender);
+          _dbus_assert (sender_policy != NULL);
+        }
+      else
+        {
+          /* Policy for inactive connections is that they can only send
+           * the hello message to the bus driver
+           */
+          if (recipient == NULL &&
+              dbus_message_has_name (message, DBUS_MESSAGE_HELLO))
             {
-              if (!add_list_to_policy (list, policy))
-                goto failed;
+              _dbus_verbose ("security check allowing %s message\n",
+                             DBUS_MESSAGE_HELLO);
+              return TRUE;
+            }
+          else
+            {
+              _dbus_verbose ("security check disallowing non-%s message\n",
+                             DBUS_MESSAGE_HELLO);
+
+              dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                              "Client tried to send a message other than %s without being registered",
+                              DBUS_MESSAGE_HELLO);
+              
+              return FALSE;
             }
-          
-          ++i;
         }
     }
+  else
+    sender_policy = NULL;
 
-  if (!dbus_connection_get_unix_user (connection, &uid))
-    goto failed;
-
-  list = _dbus_hash_table_lookup_ulong (context->rules_by_uid,
-                                        uid);
-
-  if (!add_list_to_policy (list, policy))
-    goto failed;
+  _dbus_assert ((sender != NULL && sender_policy != NULL) ||
+                (sender == NULL && sender_policy == NULL));
   
-  if (!add_list_to_policy (&context->mandatory_rules,
-                           policy))
-    goto failed;
+  if (recipient != NULL)
+    {
+      /* only the bus driver can send to an inactive recipient (as it
+       * owns no services, so other apps can't address it). Inactive
+       * recipients can receive any message.
+       */
+      if (bus_connection_is_active (recipient))
+        {
+          recipient_policy = bus_connection_get_policy (recipient);
+          _dbus_assert (recipient_policy != NULL);
+        }
+      else if (sender == NULL)
+        {
+          _dbus_verbose ("security check using NULL recipient policy for message from bus\n");
+          recipient_policy = NULL;
+        }
+      else
+        {
+          _dbus_assert_not_reached ("a message was somehow sent to an inactive recipient from a source other than the message bus\n");
+          recipient_policy = NULL;
+        }
+    }
+  else
+    recipient_policy = NULL;
+  
+  _dbus_assert ((recipient != NULL && recipient_policy != NULL) ||
+                (recipient != NULL && sender == NULL && recipient_policy == NULL) ||
+                (recipient == NULL && recipient_policy == NULL));
+  
+  if (sender_policy &&
+      !bus_client_policy_check_can_send (sender_policy,
+                                         context->registry, recipient,
+                                         message))
+    {
+      const char *dest = dbus_message_get_destination (message);
+      dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                      "A security policy in place prevents this sender "
+                      "from sending this message to this recipient, "
+                      "see message bus configuration file (rejected message "
+                      "had name \"%s\" destination \"%s\")",
+                      dbus_message_get_name (message),
+                      dest ? dest : DBUS_SERVICE_DBUS);
+      return FALSE;
+    }
 
-  bus_policy_optimize (policy);
-  
-  return policy;
-  
- failed:
-  bus_policy_unref (policy);
-  return NULL;
-}
+  if (recipient_policy &&
+      !bus_client_policy_check_can_receive (recipient_policy,
+                                            context->registry, sender,
+                                            message))
+    {
+      const char *dest = dbus_message_get_destination (message);
+      dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                      "A security policy in place prevents this recipient "
+                      "from receiving this message from this sender, "
+                      "see message bus configuration file (rejected message "
+                      "had name \"%s\" destination \"%s\")",
+                      dbus_message_get_name (message),
+                      dest ? dest : DBUS_SERVICE_DBUS);
+      return FALSE;
+    }
 
-int
-bus_context_get_activation_timeout (BusContext *context)
-{
+  /* See if limits on size have been exceeded */
+  if (recipient &&
+      dbus_connection_get_outgoing_size (recipient) >
+      context->limits.max_outgoing_bytes)
+    {
+      const char *dest = dbus_message_get_destination (message);
+      dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+                      "The destination service \"%s\" has a full message queue",
+                      dest ? dest : DBUS_SERVICE_DBUS);
+      return FALSE;
+    }
   
-  return context->activation_timeout;
+  return TRUE;
 }