2004-07-24 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / bus.c
index 91c9e6a..61f6d7d 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -1,9 +1,9 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* bus.c  message bus context object
  *
- * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.0
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
 #include "policy.h"
 #include "config-parser.h"
 #include "signals.h"
+#include "selinux.h"
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-internals.h>
 struct BusContext
 {
   int refcount;
+  char *config_file;
   char *type;
-  char *bus_env_var;
   char *address;
   char *pidfile;
+  char *user;
   DBusLoop *loop;
   DBusList *servers;
   BusConnections *connections;
@@ -49,6 +51,7 @@ struct BusContext
   BusMatchmaker *matchmaker;
   DBusUserDatabase *user_database;
   BusLimits limits;
+  unsigned int fork : 1;
 };
 
 static dbus_int32_t server_data_slot = -1;
@@ -248,45 +251,26 @@ setup_server (BusContext *context,
   return TRUE;
 }
 
-BusContext*
-bus_context_new (const DBusString *config_file,
-                 dbus_bool_t       force_fork,
-                 int               print_addr_fd,
-                 int               print_pid_fd,
-                 DBusError        *error)
+/* This code only gets executed the first time the
+   config files are parsed.  It is not executed
+   when config files are reloaded.*/
+static dbus_bool_t
+process_config_first_time_only (BusContext      *context,
+                               BusConfigParser *parser,
+                               DBusError       *error)
 {
-  BusContext *context;
   DBusList *link;
   DBusList **addresses;
-  BusConfigParser *parser;
-  DBusString full_address;
   const char *user, *pidfile;
   char **auth_mechanisms;
   DBusList **auth_mechanisms_list;
   int len;
-  
-  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  dbus_bool_t retval;
 
-  if (!_dbus_string_init (&full_address))
-    {
-      BUS_SET_OOM (error);
-      return NULL;
-    }
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
 
-  if (!dbus_server_allocate_data_slot (&server_data_slot))
-    {
-      BUS_SET_OOM (error);
-      _dbus_string_free (&full_address);
-      return NULL;
-    }
-  
-  parser = NULL;
-  context = NULL;
+  retval = FALSE;
   auth_mechanisms = NULL;
-  
-  parser = bus_config_load (config_file, TRUE, error);
-  if (parser == NULL)
-    goto failed;
 
   /* Check for an existing pid file. Of course this is a race;
    * we'd have to use fcntl() locks on the pid file to
@@ -313,38 +297,9 @@ bus_context_new (const DBusString *config_file,
        }
     }
   
-  context = dbus_new0 (BusContext, 1);
-  if (context == NULL)
-    {
-      BUS_SET_OOM (error);
-      goto failed;
-    }
-  
-  context->refcount = 1;
+  /* keep around the pid filename so we can delete it later */
+  context->pidfile = _dbus_strdup (pidfile);
 
-  /* 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 (!dbus_server_allocate_data_slot (&server_data_slot))
-    _dbus_assert_not_reached ("second ref of server data slot failed");
-  
-  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)
-    {
-      BUS_SET_OOM (error);
-      goto failed;
-    }
-  
   /* Build an array of auth mechanisms */
   
   auth_mechanisms_list = bus_config_parser_get_mechanisms (parser);
@@ -356,7 +311,10 @@ bus_context_new (const DBusString *config_file,
 
       auth_mechanisms = dbus_new0 (char*, len + 1);
       if (auth_mechanisms == NULL)
-        goto failed;
+       {
+         BUS_SET_OOM (error);
+         goto failed;
+       }
       
       i = 0;
       link = _dbus_list_get_first_link (auth_mechanisms_list);
@@ -364,7 +322,10 @@ bus_context_new (const DBusString *config_file,
         {
           auth_mechanisms[i] = _dbus_strdup (link->data);
           if (auth_mechanisms[i] == NULL)
-            goto failed;
+           {
+             BUS_SET_OOM (error);
+             goto failed;
+           }
           link = _dbus_list_get_next_link (auth_mechanisms_list, link);
         }
     }
@@ -384,9 +345,15 @@ bus_context_new (const DBusString *config_file,
       
       server = dbus_server_listen (link->data, error);
       if (server == NULL)
-        goto failed;
+       {
+         _DBUS_ASSERT_ERROR_IS_SET (error);
+         goto failed;
+       }
       else if (!setup_server (context, server, auth_mechanisms, error))
-        goto failed;
+       {
+         _DBUS_ASSERT_ERROR_IS_SET (error);
+         goto failed;
+       }
 
       if (!_dbus_list_append (&context->servers, server))
         {
@@ -404,7 +371,59 @@ bus_context_new (const DBusString *config_file,
       BUS_SET_OOM (error);
       goto failed;
     }
+
+  user = bus_config_parser_get_user (parser);
+  if (user != NULL)
+    {
+      context->user = _dbus_strdup (user);
+      if (context->user == NULL)
+       {
+         BUS_SET_OOM (error);
+         goto failed;
+       }
+    }
+
+  context->fork = bus_config_parser_get_fork (parser);
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  retval = TRUE;
+
+ failed:
+  dbus_free_string_array (auth_mechanisms);
+  return retval;
+}
+
+/* This code gets executed every time the config files
+   are parsed: both during BusContext construction
+   and on reloads. */
+static dbus_bool_t
+process_config_every_time (BusContext      *context,
+                          BusConfigParser *parser,
+                          dbus_bool_t      is_reload,
+                          DBusError       *error)
+{
+  DBusString full_address;
+  DBusList *link;
+  DBusHashTable *service_sid_table;
   
+  dbus_bool_t retval;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  retval = FALSE;
+
+  if (!_dbus_string_init (&full_address))
+    {
+      BUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  /* get our limits and timeout lengths */
+  bus_config_parser_get_limits (parser, &context->limits);
+
+  context->policy = bus_config_parser_steal_policy (parser);
+  _dbus_assert (context->policy != NULL);
+
   /* We have to build the address backward, so that
    * <listen> later in the config file have priority
    */
@@ -440,12 +459,149 @@ bus_context_new (const DBusString *config_file,
       link = _dbus_list_get_prev_link (&context->servers, link);
     }
 
+  if (is_reload)
+    dbus_free (context->address);
+
   if (!_dbus_string_copy_data (&full_address, &context->address))
     {
       BUS_SET_OOM (error);
       goto failed;
     }
 
+  /* Create activation subsystem */
+  
+  if (is_reload)
+    bus_activation_unref (context->activation);
+  
+  context->activation = bus_activation_new (context, &full_address,
+                                            bus_config_parser_get_service_dirs (parser),
+                                            error);
+  if (context->activation == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+
+  service_sid_table = bus_config_parser_steal_service_sid_table (parser);
+  bus_registry_set_service_sid_table (context->registry,
+                                      service_sid_table);
+  _dbus_hash_table_unref (service_sid_table);
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  retval = TRUE;
+
+ failed:
+  _dbus_string_free (&full_address);
+  return retval;
+}
+
+static dbus_bool_t
+load_config (BusContext *context,
+            dbus_bool_t is_reload,
+            DBusError  *error)
+{
+  BusConfigParser *parser;
+  DBusString config_file;
+  dbus_bool_t retval;
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  retval = FALSE;
+  parser = NULL;
+
+  _dbus_string_init_const (&config_file, context->config_file);
+  parser = bus_config_load (&config_file, TRUE, NULL, error);
+  if (parser == NULL)
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+  
+  if (!is_reload && !process_config_first_time_only (context, parser, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+
+  if (!process_config_every_time (context, parser, is_reload, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+  retval = TRUE;
+
+ failed:
+  if (parser)
+    bus_config_parser_unref (parser);
+  return retval;
+}
+
+BusContext*
+bus_context_new (const DBusString *config_file,
+                 dbus_bool_t       force_fork,
+                 int               print_addr_fd,
+                 int               print_pid_fd,
+                 DBusError        *error)
+{
+  BusContext *context;
+  
+  _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+  if (!dbus_server_allocate_data_slot (&server_data_slot))
+    {
+      BUS_SET_OOM (error);
+      return NULL;
+    }
+
+  context = dbus_new0 (BusContext, 1);
+  if (context == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+  context->refcount = 1;
+
+  if (!_dbus_string_copy_data (config_file, &context->config_file))
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+
+  context->loop = _dbus_loop_new ();
+  if (context->loop == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+
+  context->registry = bus_registry_new (context);
+  if (context->registry == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+  
+  if (!load_config (context, FALSE, error))
+    {
+      _DBUS_ASSERT_ERROR_IS_SET (error);
+      goto failed;
+    }
+  
+  /* we need another ref of the server data slot for the context
+   * to own
+   */
+  if (!dbus_server_allocate_data_slot (&server_data_slot))
+    _dbus_assert_not_reached ("second ref of server data slot failed");
+
+  context->user_database = _dbus_user_database_new ();
+  if (context->user_database == NULL)
+    {
+      BUS_SET_OOM (error);
+      goto failed;
+    }
+  
   /* Note that we don't know whether the print_addr_fd is
    * one of the sockets we're using to listen on, or some
    * other random thing. But I think the answer is "don't do
@@ -488,17 +644,6 @@ bus_context_new (const DBusString *config_file,
       _dbus_string_free (&addr);
     }
   
-  /* Create activation subsystem */
-  
-  context->activation = bus_activation_new (context, &full_address,
-                                            bus_config_parser_get_service_dirs (parser),
-                                            error);
-  if (context->activation == NULL)
-    {
-      _DBUS_ASSERT_ERROR_IS_SET (error);
-      goto failed;
-    }
-
   context->connections = bus_connections_new (context);
   if (context->connections == NULL)
     {
@@ -506,13 +651,6 @@ bus_context_new (const DBusString *config_file,
       goto failed;
     }
 
-  context->registry = bus_registry_new (context);
-  if (context->registry == NULL)
-    {
-      BUS_SET_OOM (error);
-      goto failed;
-    }
-
   context->matchmaker = bus_matchmaker_new ();
   if (context->matchmaker == NULL)
     {
@@ -520,37 +658,37 @@ bus_context_new (const DBusString *config_file,
       goto failed;
     }
   
-  context->policy = bus_config_parser_steal_policy (parser);
-  _dbus_assert (context->policy != NULL);
-  
   /* Now become a daemon if appropriate */
-  if (force_fork || bus_config_parser_get_fork (parser))
+  if (force_fork || context->fork)
     {
       DBusString u;
 
-      if (pidfile)
-        _dbus_string_init_const (&u, pidfile);
+      if (context->pidfile)
+        _dbus_string_init_const (&u, context->pidfile);
       
-      if (!_dbus_become_daemon (pidfile ? &u : NULL, error))
-        goto failed;
+      if (!_dbus_become_daemon (context->pidfile ? &u : NULL, error))
+       {
+         _DBUS_ASSERT_ERROR_IS_SET (error);
+         goto failed;
+       }
     }
   else
     {
       /* Need to write PID file for ourselves, not for the child process */
-      if (pidfile != NULL)
+      if (context->pidfile != NULL)
         {
           DBusString u;
 
-          _dbus_string_init_const (&u, pidfile);
+          _dbus_string_init_const (&u, context->pidfile);
           
           if (!_dbus_write_pid_file (&u, _dbus_getpid (), error))
-            goto failed;
+           {
+             _DBUS_ASSERT_ERROR_IS_SET (error);
+             goto failed;
+           }
         }
     }
 
-  /* keep around the pid filename so we can delete it later */
-  context->pidfile = _dbus_strdup (pidfile);
-
   /* Write PID if requested */
   if (print_pid_fd >= 0)
     {
@@ -590,13 +728,12 @@ bus_context_new (const DBusString *config_file,
   /* Here we change our credentials if required,
    * as soon as we've set up our sockets and pidfile
    */
-  user = bus_config_parser_get_user (parser);
-  if (user != NULL)
+  if (context->user != NULL)
     {
       DBusCredentials creds;
       DBusString u;
 
-      _dbus_string_init_const (&u, user);
+      _dbus_string_init_const (&u, context->user);
 
       if (!_dbus_credentials_from_username (&u, &creds) ||
           creds.uid < 0 ||
@@ -604,36 +741,40 @@ bus_context_new (const DBusString *config_file,
         {
           dbus_set_error (error, DBUS_ERROR_FAILED,
                           "Could not get UID and GID for username \"%s\"",
-                          user);
+                          context->user);
           goto failed;
         }
       
       if (!_dbus_change_identity (creds.uid, creds.gid, error))
-        goto failed;
+       {
+         _DBUS_ASSERT_ERROR_IS_SET (error);
+         goto failed;
+       }
     }
   
-  bus_config_parser_unref (parser);
-  _dbus_string_free (&full_address);
-  dbus_free_string_array (auth_mechanisms);
   dbus_server_free_data_slot (&server_data_slot);
   
   return context;
   
  failed:  
-  if (parser != NULL)
-    bus_config_parser_unref (parser);
-
   if (context != NULL)
     bus_context_unref (context);
 
-  _dbus_string_free (&full_address);
-  dbus_free_string_array (auth_mechanisms);
-
-  dbus_server_free_data_slot (&server_data_slot);
+  if (server_data_slot >= 0)
+    dbus_server_free_data_slot (&server_data_slot);
   
   return NULL;
 }
 
+dbus_bool_t
+bus_context_reload_config (BusContext *context,
+                          DBusError  *error)
+{
+  return load_config (context,
+                     TRUE, /* yes, we are re-loading */
+                     error);
+}
+
 static void
 shutdown_server (BusContext *context,
                  DBusServer *server)
@@ -671,11 +812,13 @@ bus_context_shutdown (BusContext  *context)
     }
 }
 
-void
+BusContext *
 bus_context_ref (BusContext *context)
 {
   _dbus_assert (context->refcount > 0);
   context->refcount += 1;
+
+  return context;
 }
 
 void
@@ -737,8 +880,10 @@ bus_context_unref (BusContext *context)
           context->matchmaker = NULL;
         }
       
+      dbus_free (context->config_file);
       dbus_free (context->type);
       dbus_free (context->address);
+      dbus_free (context->user);
 
       if (context->pidfile)
        {
@@ -753,7 +898,8 @@ bus_context_unref (BusContext *context)
           dbus_free (context->pidfile); 
        }
 
-      _dbus_user_database_unref (context->user_database);
+      if (context->user_database != NULL)
+       _dbus_user_database_unref (context->user_database);
       
       dbus_free (context);
 
@@ -819,6 +965,12 @@ bus_context_allow_user (BusContext   *context,
                                 uid);
 }
 
+BusPolicy *
+bus_context_get_policy (BusContext *context)
+{
+  return context->policy;
+}
+
 BusClientPolicy*
 bus_context_create_client_policy (BusContext      *context,
                                   DBusConnection  *connection,
@@ -921,7 +1073,8 @@ bus_context_check_security_policy (BusContext     *context,
   
   /* dispatch.c was supposed to ensure these invariants */
   _dbus_assert (dbus_message_get_destination (message) != NULL ||
-                type == DBUS_MESSAGE_TYPE_SIGNAL);
+                type == DBUS_MESSAGE_TYPE_SIGNAL ||
+                (sender == NULL && !bus_connection_is_active (proposed_recipient)));
   _dbus_assert (type == DBUS_MESSAGE_TYPE_SIGNAL ||
                 addressed_recipient != NULL ||
                 strcmp (dbus_message_get_destination (message), DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) == 0);
@@ -948,6 +1101,28 @@ bus_context_check_security_policy (BusContext     *context,
   
   if (sender != NULL)
     {
+      /* First verify the SELinux access controls.  If allowed then
+       * go on with the standard checks.
+       */
+      if (!bus_selinux_allows_send (sender, proposed_recipient))
+        {
+          const char *dest = dbus_message_get_destination (message);
+          dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                          "An SELinux policy prevents this sender "
+                          "from sending this message to this recipient "
+                          "(rejected message had interface \"%s\" "
+                          "member \"%s\" error name \"%s\" destination \"%s\")",
+                          dbus_message_get_interface (message) ?
+                          dbus_message_get_interface (message) : "(unset)",
+                          dbus_message_get_member (message) ?
+                          dbus_message_get_member (message) : "(unset)",
+                          dbus_message_get_error_name (message) ?
+                          dbus_message_get_error_name (message) : "(unset)",
+                          dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS);
+          _dbus_verbose ("SELinux security check denying send to service\n");
+          return FALSE;
+        }
+       
       if (bus_connection_is_active (sender))
         {
           sender_policy = bus_connection_get_policy (sender);
@@ -1049,7 +1224,9 @@ bus_context_check_security_policy (BusContext     *context,
   
   if (sender_policy &&
       !bus_client_policy_check_can_send (sender_policy,
-                                         context->registry, proposed_recipient,
+                                         context->registry,
+                                         requested_reply,
+                                         proposed_recipient,
                                          message))
     {
       const char *dest = dbus_message_get_destination (message);