2004-07-24 Havoc Pennington <hp@redhat.com>
authorHavoc Pennington <hp@redhat.com>
Fri, 30 Jul 2004 05:59:34 +0000 (05:59 +0000)
committerHavoc Pennington <hp@redhat.com>
Fri, 30 Jul 2004 05:59:34 +0000 (05:59 +0000)
SELinux support from Matthew Rickard <mjricka@epoch.ncsc.mil>

* bus/selinux.c, bus/selinux.h: new file encapsulating selinux
functionality

* configure.in: add --enable-selinux

* bus/policy.c (bus_policy_merge): add FIXME to a comment

* bus/main.c (main): initialize and shut down selinux

* bus/connection.c: store SELinux ID on each connection, to avoid
repeated getting of the string context and converting it into
an ID

* bus/bus.c (bus_context_get_policy): new accessor, though it
isn't used
(bus_context_check_security_policy): check whether the security
context of sender connection can send to the security context of
recipient connection

* bus/config-parser.c: add parsing for <selinux> and <associate>

* dbus/dbus-transport.c (_dbus_transport_get_unix_fd): to
implement dbus_connection_get_unix_fd()

* dbus/dbus-connection.c (dbus_connection_get_unix_fd): new
function, used by the selinux stuff

24 files changed:
ChangeLog
bus/Makefile.am
bus/bus.c
bus/bus.h
bus/config-parser.c
bus/config-parser.h
bus/connection.c
bus/connection.h
bus/dbus-daemon-1.1.in
bus/main.c
bus/policy.c
bus/selinux.c [new file with mode: 0644]
bus/selinux.h [new file with mode: 0644]
bus/services.c
bus/services.h
bus/test-main.c
configure.in
dbus/dbus-connection.c
dbus/dbus-connection.h
dbus/dbus-transport-protected.h
dbus/dbus-transport-unix.c
dbus/dbus-transport.c
dbus/dbus-transport.h
test/data/valid-config-files/basic.conf

index 88afd03..90fa3f4 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2004-07-24  Havoc Pennington  <hp@redhat.com>
+
+       SELinux support from Matthew Rickard <mjricka@epoch.ncsc.mil>
+
+       * bus/selinux.c, bus/selinux.h: new file encapsulating selinux
+       functionality
+
+       * configure.in: add --enable-selinux
+       
+       * bus/policy.c (bus_policy_merge): add FIXME to a comment
+
+       * bus/main.c (main): initialize and shut down selinux
+
+       * bus/connection.c: store SELinux ID on each connection, to avoid 
+       repeated getting of the string context and converting it into 
+       an ID
+
+       * bus/bus.c (bus_context_get_policy): new accessor, though it
+       isn't used
+       (bus_context_check_security_policy): check whether the security
+       context of sender connection can send to the security context of
+       recipient connection
+
+       * bus/config-parser.c: add parsing for <selinux> and <associate>
+       
+       * dbus/dbus-transport.c (_dbus_transport_get_unix_fd): to
+       implement dbus_connection_get_unix_fd()
+
+       * dbus/dbus-connection.c (dbus_connection_get_unix_fd): new
+       function, used by the selinux stuff
+       
 2004-07-29  Olivier Andrieu  <oliv__a@users.sourceforge.net>
 
        * bus/config-loader-libxml.c: complete the implementation of
index a6c4b17..a9761be 100644 (file)
@@ -10,7 +10,7 @@ EFENCE=
 
 CONFIG_IN_FILES=                               \
        session.conf.in                         \
-       system.conf.in 
+       system.conf.in
 
 config_DATA=                                   \
        session.conf                            \
@@ -44,6 +44,8 @@ BUS_SOURCES=                                  \
        expirelist.h                            \
        policy.c                                \
        policy.h                                \
+       selinux.h                               \
+       selinux.c                               \
        services.c                              \
        services.h                              \
        signals.c                               \
index f3ef761..61f6d7d 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -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>
@@ -403,6 +404,7 @@ process_config_every_time (BusContext      *context,
 {
   DBusString full_address;
   DBusList *link;
+  DBusHashTable *service_sid_table;
   
   dbus_bool_t retval;
 
@@ -480,6 +482,11 @@ process_config_every_time (BusContext      *context,
       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;
 
@@ -569,6 +576,13 @@ 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;
+    }
+  
   if (!load_config (context, FALSE, error))
     {
       _DBUS_ASSERT_ERROR_IS_SET (error);
@@ -637,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)
     {
@@ -958,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,
@@ -1088,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);
index 7d05508..eab7934 100644 (file)
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -38,6 +38,7 @@ typedef struct BusPolicy        BusPolicy;
 typedef struct BusClientPolicy  BusClientPolicy;
 typedef struct BusPolicyRule    BusPolicyRule;
 typedef struct BusRegistry      BusRegistry;
+typedef struct BusSELinuxID     BusSELinuxID;
 typedef struct BusService       BusService;
 typedef struct BusTransaction   BusTransaction;
 typedef struct BusMatchmaker    BusMatchmaker;
@@ -78,8 +79,11 @@ BusActivation*    bus_context_get_activation                     (BusContext
 BusMatchmaker*    bus_context_get_matchmaker                     (BusContext       *context);
 DBusLoop*         bus_context_get_loop                           (BusContext       *context);
 DBusUserDatabase* bus_context_get_user_database                  (BusContext       *context);
+
 dbus_bool_t       bus_context_allow_user                         (BusContext       *context,
                                                                   unsigned long     uid);
+BusPolicy*        bus_context_get_policy                         (BusContext       *context);
+
 BusClientPolicy*  bus_context_create_client_policy               (BusContext       *context,
                                                                   DBusConnection   *connection,
                                                                   DBusError        *error);
@@ -101,5 +105,4 @@ dbus_bool_t       bus_context_check_security_policy              (BusContext
                                                                   DBusMessage      *message,
                                                                   DBusError        *error);
 
-
 #endif /* BUS_BUS_H */
index faa5b55..29fade1 100644 (file)
@@ -24,6 +24,7 @@
 #include "test.h"
 #include "utils.h"
 #include "policy.h"
+#include "selinux.h"
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-internals.h>
 #include <string.h>
@@ -44,7 +45,9 @@ typedef enum
   ELEMENT_PIDFILE,
   ELEMENT_SERVICEDIR,
   ELEMENT_INCLUDEDIR,
-  ELEMENT_TYPE
+  ELEMENT_TYPE,
+  ELEMENT_SELINUX,
+  ELEMENT_ASSOCIATE
 } ElementType;
 
 typedef enum
@@ -117,6 +120,8 @@ struct BusConfigParser
 
   DBusList *included_files;  /**< Included files stack */
 
+  DBusHashTable *service_sid_table; /**< Map service names to SELinux contexts */
+
   unsigned int fork : 1; /**< TRUE to fork into daemon mode */
 
   unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
@@ -157,6 +162,10 @@ element_type_to_name (ElementType type)
       return "includedir";
     case ELEMENT_TYPE:
       return "type";
+    case ELEMENT_SELINUX:
+      return "selinux";
+    case ELEMENT_ASSOCIATE:
+      return "associate";
     }
 
   _dbus_assert_not_reached ("bad element type");
@@ -235,6 +244,7 @@ merge_included (BusConfigParser *parser,
                 DBusError       *error)
 {
   DBusList *link;
+  DBusHashTable *table;
 
   if (!bus_policy_merge (parser->policy,
                          included->policy))
@@ -242,6 +252,17 @@ merge_included (BusConfigParser *parser,
       BUS_SET_OOM (error);
       return FALSE;
     }
+
+  table = bus_selinux_id_table_union (parser->service_sid_table,
+                                      included->service_sid_table);
+  if (table == NULL)
+    {
+      BUS_SET_OOM (error);
+      return FALSE;
+    }
+
+  _dbus_hash_table_unref (parser->service_sid_table);
+  parser->service_sid_table = table;
   
   if (included->user != NULL)
     {
@@ -317,12 +338,17 @@ bus_config_parser_new (const DBusString      *basedir,
     }
 
   if (((parser->policy = bus_policy_new ()) == NULL) ||
-      !_dbus_string_copy (basedir, 0, &parser->basedir, 0))
+      !_dbus_string_copy (basedir, 0, &parser->basedir, 0) ||
+      ((parser->service_sid_table = bus_selinux_id_table_new ()) == NULL))
     {
       if (parser->policy)
         bus_policy_unref (parser->policy);
       
       _dbus_string_free (&parser->basedir);
+
+      if (parser->service_sid_table == NULL)
+        _dbus_hash_table_unref (parser->service_sid_table);
+      
       dbus_free (parser);
       return NULL;
     }
@@ -428,6 +454,9 @@ bus_config_parser_unref (BusConfigParser *parser)
       if (parser->policy)
         bus_policy_unref (parser->policy);
 
+      if (parser->service_sid_table)
+        _dbus_hash_table_unref (parser->service_sid_table);
+      
       dbus_free (parser);
     }
 }
@@ -658,7 +687,7 @@ start_busconfig_child (BusConfigParser   *parser,
           BUS_SET_OOM (error);
           return FALSE;
         }
-
+      
       return TRUE;
     }
   else if (strcmp (element_name, "includedir") == 0)
@@ -843,6 +872,22 @@ start_busconfig_child (BusConfigParser   *parser,
 
       return TRUE;
     }
+  else if (strcmp (element_name, "selinux") == 0)
+    {
+      Element *e;
+      const char *name;
+
+      if (!check_no_attributes (parser, "selinux", attribute_names, attribute_values, error))
+        return FALSE;
+
+      if (push_element (parser, ELEMENT_SELINUX) == NULL)
+        {
+          BUS_SET_OOM (error);
+          return FALSE;
+        }
+
+      return TRUE;
+    }
   else
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
@@ -1390,6 +1435,58 @@ start_policy_child (BusConfigParser   *parser,
     }
 }
 
+static dbus_bool_t
+start_selinux_child (BusConfigParser   *parser,
+                     const char        *element_name,
+                     const char       **attribute_names,
+                     const char       **attribute_values,
+                     DBusError         *error)
+{
+  if (strcmp (element_name, "associate") == 0)
+    {
+      const char *own;
+      const char *context;
+      
+      if (!locate_attributes (parser, "associate",
+                              attribute_names,
+                              attribute_values,
+                              error,
+                              "own", &own,
+                              "context", &context,
+                              NULL))
+        return FALSE;
+      
+      if (push_element (parser, ELEMENT_ASSOCIATE) == NULL)
+        {
+          BUS_SET_OOM (error);
+          return FALSE;
+        }
+
+      if (own == NULL || context == NULL)
+        {
+          dbus_set_error (error, DBUS_ERROR_FAILED,
+                          "Element <associate> must have attributes own=\"<servicename>\" and context=\"<selinux context>\"");
+          return FALSE;
+        }
+
+      if (!bus_selinux_id_table_insert (parser->service_sid_table,
+                                        own, context))
+        {
+          BUS_SET_OOM (error);
+          return FALSE;
+        }
+      
+      return TRUE;
+    }
+  else
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Element <%s> not allowed inside <%s> in configuration file",
+                      element_name, "selinux");
+      return FALSE;
+    }
+}
+
 dbus_bool_t
 bus_config_parser_start_element (BusConfigParser   *parser,
                                  const char        *element_name,
@@ -1440,6 +1537,12 @@ bus_config_parser_start_element (BusConfigParser   *parser,
                                  attribute_names, attribute_values,
                                  error);
     }
+  else if (t == ELEMENT_SELINUX)
+    {
+      return start_selinux_child (parser, element_name,
+                                  attribute_names, attribute_values,
+                                  error);
+    }
   else
     {
       dbus_set_error (error, DBUS_ERROR_FAILED,
@@ -1635,6 +1738,8 @@ bus_config_parser_end_element (BusConfigParser   *parser,
     case ELEMENT_ALLOW:
     case ELEMENT_DENY:
     case ELEMENT_FORK:
+    case ELEMENT_SELINUX:
+    case ELEMENT_ASSOCIATE:
       break;
     }
 
@@ -1867,6 +1972,8 @@ bus_config_parser_content (BusConfigParser   *parser,
     case ELEMENT_ALLOW:
     case ELEMENT_DENY:
     case ELEMENT_FORK:
+    case ELEMENT_SELINUX:
+    case ELEMENT_ASSOCIATE:
       if (all_whitespace (content))
         return TRUE;
       else
@@ -2160,6 +2267,20 @@ bus_config_parser_get_limits (BusConfigParser *parser,
   *limits = parser->limits;
 }
 
+DBusHashTable*
+bus_config_parser_steal_service_sid_table (BusConfigParser *parser)
+{
+  DBusHashTable *table;
+
+  _dbus_assert (parser->service_sid_table != NULL); /* can only steal once */
+
+  table = parser->service_sid_table;
+
+  parser->service_sid_table = NULL;
+
+  return table;
+}
+
 #ifdef DBUS_BUILD_TESTS
 #include <stdio.h>
 
@@ -2494,6 +2615,8 @@ config_parsers_equal (const BusConfigParser *a,
   
   /* FIXME: compare policy */
 
+  /* FIXME: compare service selinux ID table */
+
   if (! limits_equal (&a->limits, &b->limits))
     return FALSE;
 
index a48eda1..77b9255 100644 (file)
@@ -29,6 +29,7 @@
 #include <dbus/dbus.h>
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
 #include "bus.h"
 
 /* Whatever XML library we're using just pushes data into this API */
@@ -70,6 +71,8 @@ BusPolicy*  bus_config_parser_steal_policy     (BusConfigParser *parser);
 void        bus_config_parser_get_limits       (BusConfigParser *parser,
                                                 BusLimits       *limits);
 
+DBusHashTable* bus_config_parser_steal_service_sid_table (BusConfigParser *parser);
+
 /* Loader functions (backended off one of the XML parsers).  Returns a
  * finished ConfigParser.
  */
index 6b4fbe7..b751cca 100644 (file)
@@ -27,6 +27,7 @@
 #include "utils.h"
 #include "signals.h"
 #include "expirelist.h"
+#include "selinux.h"
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-timeout.h>
@@ -75,6 +76,8 @@ typedef struct
   DBusPreallocatedSend *oom_preallocated;
   BusClientPolicy *policy;
 
+  BusSELinuxID *selinux_id;
+
   long connection_tv_sec;  /**< Time when we connected (seconds component) */
   long connection_tv_usec; /**< Time when we connected (microsec component) */
   int stamp;               /**< connections->stamp last time we were traversed */
@@ -401,6 +404,9 @@ free_connection_data (void *data)
 
   if (d->policy)
     bus_client_policy_unref (d->policy);
+
+  if (d->selinux_id)
+    bus_selinux_id_unref (d->selinux_id);
   
   dbus_free (d->name);
   
@@ -539,6 +545,7 @@ bus_connections_setup_connection (BusConnections *connections,
 {
   BusConnectionData *d;
   dbus_bool_t retval;
+  DBusError error;
   
   d = dbus_new0 (BusConnectionData, 1);
   
@@ -562,6 +569,20 @@ bus_connections_setup_connection (BusConnections *connections,
     }
 
   retval = FALSE;
+
+  dbus_error_init (&error);
+  d->selinux_id = bus_selinux_init_connection_id (connection,
+                                                  &error);
+  if (dbus_error_is_set (&error))
+    {
+      /* This is a bit bogus because we pretend all errors
+       * are OOM; this is done because we know that in bus.c
+       * an OOM error disconnects the connection, which is
+       * the same thing we want on any other error.
+       */
+      dbus_error_free (&error);
+      goto out;
+    }
   
   if (!dbus_connection_set_watch_functions (connection,
                                             add_connection_watch,
@@ -639,7 +660,11 @@ bus_connections_setup_connection (BusConnections *connections,
 
  out:
   if (!retval)
-    {      
+    {
+      if (d->selinux_id)
+        bus_selinux_id_unref (d->selinux_id);
+      d->selinux_id = NULL;
+      
       if (!dbus_connection_set_watch_functions (connection,
                                                 NULL, NULL, NULL,
                                                 connection,
@@ -1008,6 +1033,18 @@ bus_connection_get_matchmaker (DBusConnection *connection)
   return bus_context_get_matchmaker (d->connections->context);
 }
 
+BusSELinuxID*
+bus_connection_get_selinux_id (DBusConnection *connection)
+{
+  BusConnectionData *d;
+
+  d = BUS_CONNECTION_DATA (connection);
+
+  _dbus_assert (d != NULL);
+
+  return d->selinux_id;
+}
+
 /**
  * Checks whether the connection is registered with the message bus.
  *
index 1d93957..ee1291f 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- mode: C; c-file-style: "gnu" -*- */
 /* connection.h  Client connections
  *
- * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003, 2004  Red Hat, Inc.
  *
  * Licensed under the Academic Free License version 2.0
  * 
@@ -50,6 +50,7 @@ BusConnections* bus_connection_get_connections    (DBusConnection
 BusRegistry*    bus_connection_get_registry       (DBusConnection               *connection);
 BusActivation*  bus_connection_get_activation     (DBusConnection               *connection);
 BusMatchmaker*  bus_connection_get_matchmaker     (DBusConnection               *connection);
+BusSELinuxID*   bus_connection_get_selinux_id     (DBusConnection               *connection);
 dbus_bool_t     bus_connections_check_limits      (BusConnections               *connections,
                                                    DBusConnection               *requesting_completion,
                                                    DBusError                    *error);
index 390d145..893e241 100644 (file)
@@ -463,6 +463,110 @@ received" are evaluated separately.
 Be careful with send_interface/receive_interface, because the 
 interface field in messages is optional.
 
+.TP
+.I "<selinux>"
+
+.PP
+The <selinux> element contains settings related to Security Enhanced Linux.
+More details below.
+
+.TP
+.I "<associate>"
+
+.PP
+An <associate> element appears below an <selinux> element and
+creates a mapping. Right now only one kind of association is possible:
+.nf
+   <associate own="org.freedesktop.Foobar" context="foo_t"/> 
+.fi
+
+.PP
+This means that if a connection asks to own the service
+"org.freedesktop.Foobar" then the source context will be the context
+of the connection and the target context will be "foo_t" - see the 
+short discussion of SELinux below.
+
+.PP
+Note, the context here is the target context when acquiring a service,
+NOT the context of the connection owning the service.
+
+.PP
+There's currently no way to set a default for owning any service, if
+we add this syntax it will look like:
+.nf
+   <associate own="*" context="foo_t"/> 
+.fi
+If you find a reason this is useful, let the developers know.
+Right now the default will be the security context of the bus itself.
+
+.PP
+If two <associate> elements specify the same service name, 
+the element appearing later in the configuration file will 
+be used.
+
+.SH SELinux
+
+.PP
+See http://www.nsa.gov/selinux/ for full details on SELinux. Some useful excerpts:
+
+.IP "" 8
+Every subject (process) and object (e.g. file, socket, IPC object,
+etc) in the system is assigned a collection of security attributes,
+known as a security context. A security context contains all of the
+security attributes associated with a particular subject or object
+that are relevant to the security policy.
+
+.IP "" 8
+In order to better encapsulate security contexts and to provide
+greater efficiency, the policy enforcement code of SELinux typically
+handles security identifiers (SIDs) rather than security contexts. A
+SID is an integer that is mapped by the security server to a security
+context at runtime.
+
+.IP "" 8
+When a security decision is required, the policy enforcement code
+passes a pair of SIDs (typically the SID of a subject and the SID of
+an object, but sometimes a pair of subject SIDs or a pair of object
+SIDs), and an object security class to the security server. The object
+security class indicates the kind of object, e.g. a process, a regular
+file, a directory, a TCP socket, etc.
+
+.IP "" 8
+Access decisions specify whether or not a permission is granted for a
+given pair of SIDs and class. Each object class has a set of
+associated permissions defined to control operations on objects with
+that class.
+
+.PP
+D-BUS performs SELinux security checks in two places.
+
+.PP
+First, any time a message is routed from one connection to another
+connection, the bus daemon will check permissions with the security context of
+the first connection as source, security context of the second connection
+as target, object class "dbus" and requested permission "send_msg".
+
+.PP
+If a security context is not available for a connection 
+(impossible when using UNIX domain sockets), then the target 
+context used is the context of the bus daemon itself.
+There is currently no way to change this default, because we're 
+assuming that only UNIX domain sockets will be used to 
+connect to the systemwide bus. If this changes, we'll 
+probably add a way to set the default connection context.
+
+.PP
+Second, any time a connection asks to own a service, 
+the bus daemon will check permissions with the security 
+context of the connection as source, the security context specified
+for the service name with an <associate> element as target, object 
+class "dbus" and requested permission "acquire_svc".
+
+.PP
+If the service name has no security context associated in the 
+configuration file, the security context of the bus daemon 
+itself will be used.
+
 .SH AUTHOR
 See http://www.freedesktop.org/software/dbus/doc/AUTHORS
 
index 8f17532..32a6161 100644 (file)
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <signal.h>
 #include <errno.h>
+#include "selinux.h"
 
 static BusContext *context;
 
@@ -371,7 +372,13 @@ main (int argc, char **argv)
           print_pid_fd = val;
         }
     }
-  
+
+  if (!bus_selinux_init ())
+    {
+      _dbus_warn ("SELinux initialization failed\n");
+      exit (1);
+    }
+
   dbus_error_init (&error);
   context = bus_context_new (&config_file, force_fork,
                              print_addr_fd, print_pid_fd,
@@ -395,6 +402,7 @@ main (int argc, char **argv)
   
   bus_context_shutdown (context);
   bus_context_unref (context);
+  bus_selinux_shutdown ();
 
   return 0;
 }
index 3c7bba9..c7359c8 100644 (file)
@@ -177,7 +177,7 @@ bus_policy_new (void)
                                                free_rule_list_func);
   if (policy->rules_by_gid == NULL)
     goto failed;
-  
+
   return policy;
   
  failed:
@@ -594,9 +594,10 @@ dbus_bool_t
 bus_policy_merge (BusPolicy *policy,
                   BusPolicy *to_absorb)
 {
-  /* Not properly atomic, but as used for configuration files
-   * we don't rely on it.
-   */  
+  /* FIXME Not properly atomic, but as used for configuration files we
+   * don't rely on it quite so much.
+   */
+  
   if (!append_copy_of_policy_list (&policy->default_rules,
                                    &to_absorb->default_rules))
     return FALSE;
@@ -670,7 +671,7 @@ bus_client_policy_unref (BusClientPolicy *policy)
                           NULL);
 
       _dbus_list_clear (&policy->rules);
-      
+
       dbus_free (policy);
     }
 }
diff --git a/bus/selinux.c b/bus/selinux.c
new file mode 100644 (file)
index 0000000..24650ed
--- /dev/null
@@ -0,0 +1,658 @@
+/* selinux.c  SELinux security checks for D-BUS
+ *
+ * Author: Matthew Rickard <mjricka@epoch.ncsc.mil>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * 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
+ *
+ */
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-string.h>
+#include "selinux.h"
+#include "services.h"
+#include "policy.h"
+#include "config-parser.h"
+
+#ifdef HAVE_SELINUX
+#include <errno.h>
+#include <syslog.h>
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include <selinux/av_permissions.h>
+#include <selinux/flask.h>
+#include <stdarg.h>
+#endif /* HAVE_SELINUX */
+
+#define BUS_SID_FROM_SELINUX(sid)  ((BusSELinuxID*) (sid))
+#define SELINUX_SID_FROM_BUS(sid)  ((security_id_t) (sid))
+
+#ifdef HAVE_SELINUX
+/* Store the value telling us if SELinux is enabled in the kernel. */
+static dbus_bool_t selinux_enabled = FALSE;
+
+/* Store an avc_entry_ref to speed AVC decisions. */
+static struct avc_entry_ref aeref;
+
+static security_id_t bus_sid = SECSID_WILD;
+#endif /* HAVE_SELINUX */
+
+/**
+ * Log callback to log denial messages from the AVC.
+ * This is used in avc_init.  Logs to both standard
+ * error and syslogd.
+ *
+ * @param fmt the format string
+ * @param variable argument list
+ */
+#ifdef HAVE_SELINUX
+static void 
+log_callback (const char *fmt, ...) 
+{
+  va_list ap;
+  va_start(ap, fmt);
+  vsyslog (LOG_INFO, fmt, ap);
+  va_end(ap);
+}
+#endif /* HAVE_SELINUX */
+
+
+/**
+ * Initialize the user space access vector cache (AVC) for D-BUS and set up
+ * logging callbacks.
+ */
+dbus_bool_t
+bus_selinux_init (void)
+{
+#ifdef HAVE_SELINUX
+  struct avc_log_callback log_cb = {(void*)log_callback, NULL};
+  int r;
+  char *bus_context;
+
+  _dbus_assert (bus_sid == SECSID_WILD);
+  
+  /* Determine if we are running an SELinux kernel. */
+  r = is_selinux_enabled ();
+  if (r < 0)
+    {
+      _dbus_warn ("Could not tell if SELinux is enabled: %s\n",
+                  _dbus_strerror (errno));
+      return FALSE;
+    }
+
+  selinux_enabled = r != 0;
+
+  if (!selinux_enabled)
+    {
+      _dbus_verbose ("SELinux not enabled in this kernel.\n");
+      return TRUE;
+    }
+
+  _dbus_verbose ("SELinux is enabled in this kernel.\n");
+
+  avc_entry_ref_init (&aeref);
+  if (avc_init ("avc", NULL, &log_cb, NULL, NULL) < 0)
+    {
+      _dbus_warn ("Failed to start Access Vector Cache (AVC).\n");
+      return FALSE;
+    }
+  else
+    {
+      openlog ("dbus", LOG_PERROR, LOG_USER);
+      _dbus_verbose ("Access Vector Cache (AVC) started.\n");
+    }
+
+  bus_context = NULL;
+  bus_sid = SECSID_WILD;
+
+  if (getcon (&bus_context) < 0)
+    {
+      _dbus_verbose ("Error getting context of bus: %s\n",
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+      
+  if (avc_context_to_sid (bus_context, &bus_sid) < 0)
+    {
+      _dbus_verbose ("Error getting SID from bus context: %s\n",
+                     _dbus_strerror (errno));
+      freecon (bus_context);
+      return FALSE;
+    }
+
+  freecon (bus_context);
+  
+  return TRUE;
+#else
+  return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+
+/**
+ * Decrement SID reference count.
+ * 
+ * @param sid the SID to decrement
+ */
+void
+bus_selinux_id_unref (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+  if (!selinux_enabled)
+    return;
+
+  _dbus_assert (sid != NULL);
+  
+  sidput (SELINUX_SID_FROM_BUS (sid));
+#endif /* HAVE_SELINUX */
+}
+
+void
+bus_selinux_id_ref (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+  if (!selinux_enabled)
+    return;
+
+  _dbus_assert (sid != NULL);
+  
+  sidget (SELINUX_SID_FROM_BUS (sid));
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Determine if the SELinux security policy allows the given sender
+ * security context to go to the given recipient security context.
+ * This function determines if the requested permissions are to be
+ * granted from the connection to the message bus or to another
+ * optionally supplied security identifier (e.g. for a service
+ * context).  Currently these permissions are either send_msg or
+ * acquire_svc in the dbus class.
+ *
+ * @param sender_sid source security context
+ * @param override_sid is the target security context.  If SECSID_WILD this will
+ *        use the context of the bus itself (e.g. the default).
+ * @param target_class is the target security class.
+ * @param requested is the requested permissions.
+ * @returns #TRUE if security policy allows the send.
+ */
+#ifdef HAVE_SELINUX
+static dbus_bool_t
+bus_selinux_check (BusSELinuxID        *sender_sid,
+                   BusSELinuxID        *override_sid,
+                   security_class_t     target_class,
+                   access_vector_t      requested)
+{
+  if (!selinux_enabled)
+    return TRUE;
+  
+  /* Make the security check.  AVC checks enforcing mode here as well. */
+  if (avc_has_perm (SELINUX_SID_FROM_BUS (sender_sid),
+                    override_sid ?
+                    SELINUX_SID_FROM_BUS (override_sid) :
+                    SELINUX_SID_FROM_BUS (bus_sid), 
+                    target_class, requested, &aeref, NULL) < 0)
+    {
+      _dbus_verbose ("SELinux denying due to security policy.\n");
+      return FALSE;
+    }
+  else
+    return TRUE;
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Returns true if the given connection can acquire a service,
+ * assuming the given security ID is needed for that service.
+ *
+ * @param connection connection that wants to own the service
+ * @param service_sid the SID of the service from the table
+ * @returns #TRUE if acquire is permitted.
+ */
+dbus_bool_t
+bus_selinux_allows_acquire_service (DBusConnection     *connection,
+                                    BusSELinuxID       *service_sid)
+{
+#ifdef HAVE_SELINUX
+  BusSELinuxID *connection_sid;
+  
+  if (!selinux_enabled)
+    return TRUE;
+
+  connection_sid = bus_connection_get_selinux_id (connection);
+  
+  return bus_selinux_check (connection_sid,
+                            service_sid,
+                            SECCLASS_DBUS,
+                            DBUS__ACQUIRE_SVC);
+#else
+  return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Check if SELinux security controls allow the message to be sent to a
+ * particular connection based on the security context of the sender and
+ * that of the receiver. The destination connection need not be the
+ * addressed recipient, it could be an "eavesdropper"
+ *
+ * @param sender the sender of the message.
+ * @param proposed_recipient the connection the message is to be sent to.
+ * @returns whether to allow the send
+ */
+dbus_bool_t
+bus_selinux_allows_send (DBusConnection     *sender,
+                         DBusConnection     *proposed_recipient)
+{
+#ifdef HAVE_SELINUX
+  BusSELinuxID *recipient_sid;
+  BusSELinuxID *sender_sid;
+
+  if (!selinux_enabled)
+    return TRUE;
+
+  sender_sid = bus_connection_get_selinux_id (sender);
+  /* A NULL proposed_recipient means the bus itself. */
+  if (proposed_recipient)
+    recipient_sid = bus_connection_get_selinux_id (proposed_recipient);
+  else
+    recipient_sid = BUS_SID_FROM_SELINUX (bus_sid);
+
+  return bus_selinux_check (sender_sid, recipient_sid,
+                            SECCLASS_DBUS, DBUS__SEND_MSG);
+#else
+  return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Gets the security context of a connection to the bus. It is up to
+ * the caller to freecon() when they are done. 
+ *
+ * @param connection the connection to get the context of.
+ * @param con the location to store the security context.
+ * @returns #TRUE if context is successfully obtained.
+ */
+#ifdef HAVE_SELINUX
+static dbus_bool_t
+bus_connection_read_selinux_context (DBusConnection     *connection,
+                                     char              **con)
+{
+  int fd;
+
+  if (!selinux_enabled)
+    return FALSE;
+
+  _dbus_assert (connection != NULL);
+  
+  if (!dbus_connection_get_unix_fd (connection, &fd))
+    {
+      _dbus_verbose ("Failed to get file descriptor of socket.\n");
+      return FALSE;
+    }
+  
+  if (getpeercon (fd, con) < 0)
+    {
+      _dbus_verbose ("Error getting context of socket peer: %s\n",
+                     _dbus_strerror (errno));
+      return FALSE;
+    }
+  
+  _dbus_verbose ("Successfully read connection context.\n");
+  return TRUE;
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Read the SELinux ID from the connection.
+ *
+ * @param connection the connection to read from
+ * @returns the SID if successfully determined, #NULL otherwise.
+ */
+BusSELinuxID*
+bus_selinux_init_connection_id (DBusConnection *connection,
+                                DBusError      *error)
+{
+#ifdef HAVE_SELINUX
+  char *con;
+  security_id_t sid;
+  
+  if (!selinux_enabled)
+    return NULL;
+
+  if (!bus_connection_read_selinux_context (connection, &con))
+    {
+      dbus_set_error (error, DBUS_ERROR_FAILED,
+                      "Failed to read an SELinux context from connection");
+      _dbus_verbose ("Error getting peer context.\n");
+      return NULL;
+    }
+
+  _dbus_verbose ("Converting context to SID to store on connection\n");
+
+  if (avc_context_to_sid (con, &sid) < 0)
+    {
+      if (errno == ENOMEM)
+        BUS_SET_OOM (error);
+      else
+        dbus_set_error (error, DBUS_ERROR_FAILED,
+                        "Error getting SID from context: %s\n",
+                        _dbus_strerror (errno));
+      
+      _dbus_warn ("Error getting SID from context: %s\n",
+                  _dbus_strerror (errno));
+      
+      freecon (con);
+      return NULL;
+    }
+  freecon (con); 
+  return BUS_SID_FROM_SELINUX (sid);
+#else
+  return NULL;
+#endif /* HAVE_SELINUX */
+}
+
+
+/* Function for freeing hash table data.  These SIDs
+ * should no longer be referenced.
+ */
+static void
+bus_selinux_id_table_free_value (BusSELinuxID *sid)
+{
+#ifdef HAVE_SELINUX
+  /* NULL sometimes due to how DBusHashTable works */
+  if (sid)
+    bus_selinux_id_unref (sid);
+#endif /* HAVE_SELINUX */
+}
+
+/**
+ * Creates a new table mapping service names to security ID.
+ * A security ID is a "compiled" security context, a security
+ * context is just a string.
+ *
+ * @returns the new table or #NULL if no memory
+ */
+DBusHashTable*
+bus_selinux_id_table_new (void)
+{
+  return _dbus_hash_table_new (DBUS_HASH_STRING,
+                               (DBusFreeFunction) dbus_free,
+                               (DBusFreeFunction) bus_selinux_id_table_free_value);
+}
+
+/** 
+ * Hashes a service name and service context into the service SID
+ * table as a string and a SID.
+ *
+ * @param service_name is the name of the service.
+ * @param service_context is the context of the service.
+ * @param service_table is the table to hash them into.
+ * @return #FALSE if not enough memory
+ */
+dbus_bool_t
+bus_selinux_id_table_insert (DBusHashTable *service_table,
+                             const char    *service_name,
+                             const char    *service_context)
+{
+#ifdef HAVE_SELINUX
+  dbus_bool_t retval;
+  security_id_t sid;
+  char *key;
+
+  if (!selinux_enabled)
+    return TRUE;
+
+  sid = SECSID_WILD;
+  retval = FALSE;
+
+  key = _dbus_strdup (service_name);
+  if (key == NULL)
+    return retval;
+  
+  if (avc_context_to_sid (service_context, &sid) < 0)
+    {
+      _dbus_assert (errno == ENOMEM);
+      goto out;
+    }
+
+  if (!_dbus_hash_table_insert_string (service_table,
+                                       key,
+                                       BUS_SID_FROM_SELINUX (sid)))
+    goto out;
+
+  _dbus_verbose ("Parsed \tservice: %s \n\t\tcontext: %s\n",
+                  key, 
+                  sid->ctx);
+
+  /* These are owned by the hash, so clear them to avoid unref */
+  key = NULL;
+  sid = SECSID_WILD;
+
+  retval = TRUE;
+  
+ out:
+  if (sid != SECSID_WILD)
+    sidput (sid);
+
+  if (key)
+    dbus_free (key);
+
+  return retval;
+#else
+  return TRUE;
+#endif /* HAVE_SELINUX */
+}
+
+
+/**
+ * Find the security identifier associated with a particular service
+ * name.  Return a pointer to this SID, or #NULL/SECSID_WILD if the
+ * service is not found in the hash table.  This should be nearly a
+ * constant time operation.  If SELinux support is not available,
+ * always return NULL.
+ *
+ * @todo This should return a const security_id_t since we don't
+ *       want the caller to mess with it.
+ *
+ * @param service_table the hash table to check for service name.
+ * @param service_name the name of the service to look for.
+ * @returns the SELinux ID associated with the service
+ */
+BusSELinuxID*
+bus_selinux_id_table_lookup (DBusHashTable    *service_table,
+                             const DBusString *service_name)
+{
+#ifdef HAVE_SELINUX
+  security_id_t sid;
+
+  sid = SECSID_WILD;     /* default context */
+
+  if (!selinux_enabled)
+    return NULL;
+  
+  _dbus_verbose ("Looking up service SID for %s\n",
+                 _dbus_string_get_const_data (service_name));
+
+  sid = _dbus_hash_table_lookup_string (service_table,
+                                        _dbus_string_get_const_data (service_name));
+
+  if (sid == SECSID_WILD)
+    _dbus_verbose ("Service %s not found\n", 
+                   _dbus_string_get_const_data (service_name));
+  else
+    _dbus_verbose ("Service %s found\n", 
+                   _dbus_string_get_const_data (service_name));
+
+  return BUS_SID_FROM_SELINUX (sid);
+#endif /* HAVE_SELINUX */
+  return NULL;
+}
+
+#ifdef HAVE_SELINUX
+static dbus_bool_t
+bus_selinux_id_table_copy_over (DBusHashTable    *dest,
+                                DBusHashTable    *override)
+{
+  const char *key;
+  char *key_copy;
+  BusSELinuxID *sid;
+  DBusHashIter iter;
+  
+  _dbus_hash_iter_init (override, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      key = _dbus_hash_iter_get_string_key (&iter);
+      sid = _dbus_hash_iter_get_value (&iter);
+
+      key_copy = _dbus_strdup (key);
+      if (key_copy == NULL)
+        return FALSE;
+
+      if (!_dbus_hash_table_insert_string (dest,
+                                           key_copy,
+                                           sid))
+        {
+          dbus_free (key_copy);
+          return FALSE;
+        }
+
+      bus_selinux_id_ref (sid);
+    }
+
+  return TRUE;
+}
+#endif /* HAVE_SELINUX */
+
+/**
+ * Creates the union of the two tables (each table maps a service
+ * name to a security ID). In case of the same service name in
+ * both tables, the security ID from "override" will be used.
+ *
+ * @param base the base table
+ * @param override the table that takes precedence in the merge
+ * @returns the new table, or #NULL if out of memory
+ */
+DBusHashTable*
+bus_selinux_id_table_union (DBusHashTable    *base,
+                            DBusHashTable    *override)
+{
+  DBusHashTable *combined_table;
+
+  combined_table = bus_selinux_id_table_new ();
+
+  if (combined_table == NULL)
+    return NULL;
+  
+#ifdef HAVE_SELINUX 
+  if (!selinux_enabled)
+    return combined_table;
+
+  if (!bus_selinux_id_table_copy_over (combined_table, base))
+    {
+      _dbus_hash_table_unref (combined_table);
+      return NULL;
+    }
+
+  if (!bus_selinux_id_table_copy_over (combined_table, override))
+    {
+      _dbus_hash_table_unref (combined_table);
+      return NULL;
+    }
+#endif /* HAVE_SELINUX */
+  
+  return combined_table;
+}
+
+/**
+ * For debugging:  Print out the current hash table of service SIDs.
+ */
+void
+bus_selinux_id_table_print (DBusHashTable *service_table)
+{
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+#ifdef HAVE_SELINUX
+  DBusHashIter iter;
+
+  if (!selinux_enabled)
+    return;
+  
+  _dbus_verbose ("Service SID Table:\n");
+  _dbus_hash_iter_init (service_table, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      const char *key = _dbus_hash_iter_get_string_key (&iter);
+      security_id_t sid = _dbus_hash_iter_get_value (&iter);
+      _dbus_verbose ("The key is %s\n", key);
+      _dbus_verbose ("The context is %s\n", sid->ctx);
+      _dbus_verbose ("The refcount is %d\n", sid->refcnt);
+    }
+#endif /* HAVE_SELINUX */
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+}
+
+
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+#ifdef HAVE_SELINUX
+/**
+ * Print out some AVC statistics.
+ */
+static void
+bus_avc_print_stats (void)
+{
+  struct avc_cache_stats cstats;
+
+  if (!selinux_enabled)
+    return;
+  
+  _dbus_verbose ("AVC Statistics:\n");
+  avc_cache_stats (&cstats);
+  avc_av_stats ();
+  _dbus_verbose ("AVC Cache Statistics:\n");
+  _dbus_verbose ("Entry lookups: %d\n", cstats.entry_lookups);
+  _dbus_verbose ("Entry hits: %d\n", cstats.entry_hits);
+  _dbus_verbose ("Entry misses %d\n", cstats.entry_misses);
+  _dbus_verbose ("Entry discards: %d\n", cstats.entry_discards);
+  _dbus_verbose ("CAV lookups: %d\n", cstats.cav_lookups);
+  _dbus_verbose ("CAV hits: %d\n", cstats.cav_hits);
+  _dbus_verbose ("CAV probes: %d\n", cstats.cav_probes);
+  _dbus_verbose ("CAV misses: %d\n", cstats.cav_misses);
+}
+#endif /* HAVE_SELINUX */
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+
+/**
+ * Destroy the AVC before we terminate.
+ */
+void
+bus_selinux_shutdown (void)
+{
+#ifdef HAVE_SELINUX
+  if (!selinux_enabled)
+    return;
+
+  sidput (bus_sid);
+  bus_sid = SECSID_WILD;
+  
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+  bus_avc_print_stats ();
+#endif /* DBUS_ENABLE_VERBOSE_MODE */
+
+  avc_destroy ();
+#endif /* HAVE_SELINUX */
+}
+
diff --git a/bus/selinux.h b/bus/selinux.h
new file mode 100644 (file)
index 0000000..79a9f98
--- /dev/null
@@ -0,0 +1,60 @@
+/* selinux.h  SELinux security check headers for D-BUS
+ *
+ * Author: Matthew Rickard <mjricka@epoch.ncsc.mil>
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * 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
+ *
+ */
+
+#ifndef BUS_SELINUX_H
+#define BUS_SELINUX_H
+
+#include <dbus/dbus-hash.h>
+#include <dbus/dbus-connection.h>
+#include "services.h"
+
+dbus_bool_t bus_selinux_init     (void);
+void        bus_selinux_shutdown (void);
+
+void bus_selinux_id_ref    (BusSELinuxID *sid);
+void bus_selinux_id_unref  (BusSELinuxID *sid);
+
+DBusHashTable* bus_selinux_id_table_new    (void);
+BusSELinuxID*  bus_selinux_id_table_lookup (DBusHashTable    *service_table,
+                                            const DBusString *service_name);
+dbus_bool_t    bus_selinux_id_table_insert (DBusHashTable    *service_table,
+                                            const char       *service_name,
+                                            const char       *service_context);
+DBusHashTable* bus_selinux_id_table_union  (DBusHashTable    *base,
+                                            DBusHashTable    *override);
+void           bus_selinux_id_table_print  (DBusHashTable    *service_table);
+
+
+
+dbus_bool_t bus_selinux_allows_acquire_service (DBusConnection *connection,
+                                                BusSELinuxID   *service_sid);
+dbus_bool_t bus_selinux_allows_send            (DBusConnection *sender,
+                                                DBusConnection *proposed_recipient);
+
+
+
+BusSELinuxID* bus_selinux_init_connection_id (DBusConnection *connection,
+                                              DBusError      *error);
+
+
+
+#endif /* BUS_SELINUX_H */
index 33caded..81e3eaf 100644 (file)
@@ -31,6 +31,8 @@
 #include "utils.h"
 #include "activation.h"
 #include "policy.h"
+#include "bus.h"
+#include "selinux.h"
 
 struct BusService
 {
@@ -51,6 +53,8 @@ struct BusRegistry
   
   DBusHashTable *service_hash;
   DBusMemPool   *service_pool;
+
+  DBusHashTable *service_sid_table;
 };
 
 BusRegistry*
@@ -75,6 +79,8 @@ bus_registry_new (BusContext *context)
   if (registry->service_pool == NULL)
     goto failed;
 
+  registry->service_sid_table = NULL;
+  
   return registry;
 
  failed:
@@ -103,7 +109,9 @@ bus_registry_unref  (BusRegistry *registry)
         _dbus_hash_table_unref (registry->service_hash);
       if (registry->service_pool)
         _dbus_mem_pool_free (registry->service_pool);
-
+      if (registry->service_sid_table)
+        _dbus_hash_table_unref (registry->service_sid_table);
+      
       dbus_free (registry);
     }
 }
@@ -263,6 +271,7 @@ bus_registry_acquire_service (BusRegistry      *registry,
   BusClientPolicy *policy;
   BusService *service;
   BusActivation  *activation;
+  BusSELinuxID *sid;
   
   retval = FALSE;
 
@@ -292,6 +301,24 @@ bus_registry_acquire_service (BusRegistry      *registry,
   policy = bus_connection_get_policy (connection);
   _dbus_assert (policy != NULL);
 
+  /* Note that if sid is #NULL then the bus's own context gets used
+   * in bus_connection_selinux_allows_acquire_service()
+   */
+  sid = bus_selinux_id_table_lookup (registry->service_sid_table,
+                                     service_name);
+
+  if (!bus_selinux_allows_acquire_service (connection, sid))
+    {
+      dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                      "Connection \"%s\" is not allowed to own the service \"%s\" due "
+                      "to SELinux policy",
+                      bus_connection_is_active (connection) ?
+                      bus_connection_get_name (connection) :
+                      "(inactive)",
+                     service_name);
+      goto out;
+    }
+      
   if (!bus_client_policy_check_can_own (policy, connection,
                                         service_name))
     {
@@ -387,6 +414,19 @@ bus_registry_acquire_service (BusRegistry      *registry,
   return retval;
 }
 
+void
+bus_registry_set_service_sid_table (BusRegistry   *registry,
+                                    DBusHashTable *table)
+{
+  _dbus_assert (registry->service_sid_table != table);
+  
+  if (registry->service_sid_table)
+    _dbus_hash_table_unref (registry->service_sid_table);
+
+  registry->service_sid_table = table;
+  _dbus_hash_table_ref (table);
+}
+
 static void
 bus_service_unlink_owner (BusService      *service,
                           DBusConnection  *owner)
index fae324d..fa7a3a1 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <dbus/dbus.h>
 #include <dbus/dbus-string.h>
+#include <dbus/dbus-hash.h>
 #include "connection.h"
 #include "bus.h"
 
@@ -55,6 +56,8 @@ dbus_bool_t  bus_registry_acquire_service (BusRegistry                 *registry
                                            dbus_uint32_t               *result,
                                            BusTransaction              *transaction,
                                            DBusError                   *error);
+void         bus_registry_set_service_sid_table (BusRegistry           *registry,
+                                                 DBusHashTable         *table);
 
 BusService*     bus_service_ref                      (BusService     *service);
 void            bus_service_unref                    (BusService     *service);
index 1626f2d..6d453b4 100644 (file)
@@ -27,6 +27,7 @@
 #include <dbus/dbus-string.h>
 #include <dbus/dbus-sysdeps.h>
 #include <dbus/dbus-internals.h>
+#include "selinux.h"
 
 #ifdef DBUS_BUILD_TESTS
 static void
@@ -69,6 +70,9 @@ main (int argc, char **argv)
       return 1;
     }
 
+  if (!bus_selinux_init ())
+    die ("could not init selinux support");
+
   _dbus_string_init_const (&test_data_dir, dir);
 
 #if 0
@@ -119,6 +123,8 @@ main (int argc, char **argv)
   check_memleaks (argv[0]);
   
   printf ("%s: Success\n", argv[0]);
+
+  bus_selinux_shutdown ();
   
   return 0;
 #else /* DBUS_BUILD_TESTS */
index 2c18dd4..67f971c 100644 (file)
@@ -38,7 +38,7 @@ AC_ARG_ENABLE(gcj,              [  --enable-gcj          build gcj bindings],ena
 AC_ARG_ENABLE(mono,             [  --enable-mono         build mono bindings],enable_mono=$enableval,enable_mono=auto)
 AC_ARG_ENABLE(mono_docs,        [  --enable-mono-docs    build mono docs],enable_mono_docs=$enableval,enable_mono_docs=auto)
 AC_ARG_ENABLE(python,           [  --enable-python       build python bindings],enable_python=$enableval,enable_python=auto)
-
+AC_ARG_ENABLE(selinux,          [  --enable-selinux      build with SELinux support],enable_selinux=$enableval,enable_selinux=auto)
 
 AC_ARG_WITH(xml,                [  --with-xml=[libxml/expat]           XML library to use])
 AC_ARG_WITH(init-scripts,       [  --with-init-scripts=[redhat]        Style of init scripts to install])
@@ -690,6 +690,40 @@ if $dbus_use_libxml; then
    XML_CFLAGS=$LIBXML_CFLAGS
 fi
 
+# SELinux detection
+if test x$enable_selinux = xno ; then
+    have_selinux=no;
+else
+    # See if we have SELinux library
+    AC_CHECK_LIB(selinux, is_selinux_enabled, 
+                 have_selinux=yes, have_selinux=no)
+
+    # see if we have the SELinux header with the new D-BUS stuff in it
+    if test x$have_selinux = xyes ; then
+        AC_EGREP_HEADER(DBUS__ACQUIRE_SVC, av_permissions.h,
+                        have_selinux=yes, have_selinux=no)
+    fi
+
+    if test x$enable_selinux = xauto ; then
+        if test x$have_selinux = xno ; then
+                AC_MSG_WARN([Sufficiently new SELinux library not found])
+        fi
+    else 
+        if test x$have_selinux = xno ; then
+                AC_MSG_ERROR([SElinux explicitly required, and SELinux library not found])
+        fi
+    fi
+fi
+
+AM_CONDITIONAL(HAVE_SELINUX, test x$have_selinux = xyes)
+
+if test x$have_selinux = xyes ; then
+    SELINUX_LIBS=-lselinux
+    AC_DEFINE(HAVE_SELINUX,1,[SELinux support])
+else
+    SELINUX_LIBS=
+fi
+
 #### Set up final flags
 DBUS_CLIENT_CFLAGS=
 DBUS_CLIENT_LIBS=
@@ -697,7 +731,7 @@ AC_SUBST(DBUS_CLIENT_CFLAGS)
 AC_SUBST(DBUS_CLIENT_LIBS)
 
 DBUS_BUS_CFLAGS=$XML_CFLAGS
-DBUS_BUS_LIBS=$XML_LIBS
+DBUS_BUS_LIBS="$XML_LIBS $SELINUX_LIBS"
 AC_SUBST(DBUS_BUS_CFLAGS)
 AC_SUBST(DBUS_BUS_LIBS)
 
@@ -1076,7 +1110,6 @@ fi
 
 AM_CONDITIONAL(HAVE_PYTHON, test x$have_python = xyes)
 
-
 AC_OUTPUT([
 Doxyfile
 dbus/dbus-arch-deps.h
@@ -1167,6 +1200,7 @@ echo "
         Building Qt bindings:     ${have_qt}
         Building GLib bindings:   ${have_glib}
         Building Python bindings: ${have_python}
+        Building SELinux support: ${have_selinux}
        Building Mono bindings:   ${enable_mono}
        Building Mono docs:       ${enable_mono_docs}
         Building GTK+ tools:      ${have_gtk}
index 58ab790..91a2100 100644 (file)
@@ -2953,6 +2953,37 @@ dbus_connection_set_dispatch_status_function (DBusConnection             *connec
 }
 
 /**
+ * Get the UNIX file descriptor of the connection, if any.  This can
+ * be used for SELinux access control checks with getpeercon() for
+ * example. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a file descriptor. So for adding descriptors
+ * to the main loop, use dbus_watch_get_fd() and so forth.
+ *
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_unix_fd (DBusConnection *connection,
+                             int            *fd)
+{
+  dbus_bool_t retval;
+
+  _dbus_return_val_if_fail (connection != NULL, FALSE);
+  _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+  
+  CONNECTION_LOCK (connection);
+  
+  retval = _dbus_transport_get_unix_fd (connection->transport,
+                                        fd);
+
+  CONNECTION_UNLOCK (connection);
+
+  return retval;
+}
+
+/**
  * Gets the UNIX user ID of the connection if any.
  * Returns #TRUE if the uid is filled in.
  * Always returns #FALSE on non-UNIX platforms.
index 12de0c0..c8c66a3 100644 (file)
@@ -242,6 +242,9 @@ dbus_bool_t dbus_connection_list_registered        (DBusConnection
                                                     const char                  *parent_path,
                                                     char                      ***child_entries);
 
+dbus_bool_t dbus_connection_get_unix_fd            (DBusConnection              *connection,
+                                                    int                         *fd);
+
 DBUS_END_DECLS;
 
 #endif /* DBUS_CONNECTION_H */
index 409e683..4a9ce96 100644 (file)
@@ -71,6 +71,10 @@ struct DBusTransportVTable
 
   void        (* live_messages_changed) (DBusTransport *transport);
   /**< Outstanding messages counter changed */
+
+  dbus_bool_t (* get_unix_fd) (DBusTransport *transport,
+                               int           *fd_p);
+  /**< Get UNIX file descriptor */
 };
 
 /**
@@ -102,6 +106,7 @@ struct DBusTransport
   
   DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */
   void *unix_user_data;                         /**< Data for unix_user_function */
+  
   DBusFreeFunction free_unix_user_data;         /**< Function to free unix_user_data */
   
   unsigned int disconnected : 1;              /**< #TRUE if we are disconnected. */
index 37825f1..3447ae1 100644 (file)
@@ -948,6 +948,18 @@ unix_live_messages_changed (DBusTransport *transport)
   check_read_watch (transport);
 }
 
+
+static dbus_bool_t
+unix_get_unix_fd (DBusTransport *transport,
+                  int           *fd_p)
+{
+  DBusTransportUnix *unix_transport = (DBusTransportUnix*) transport;
+  
+  *fd_p = unix_transport->fd;
+
+  return TRUE;
+}
+
 static DBusTransportVTable unix_vtable = {
   unix_finalize,
   unix_handle_watch,
@@ -955,7 +967,8 @@ static DBusTransportVTable unix_vtable = {
   unix_connection_set,
   unix_messages_pending,
   unix_do_iteration,
-  unix_live_messages_changed
+  unix_live_messages_changed,
+  unix_get_unix_fd
 };
 
 /**
index ada960d..dde1c6d 100644 (file)
@@ -637,6 +637,35 @@ _dbus_transport_messages_pending (DBusTransport  *transport,
 }
 
 /**
+ * Get the UNIX file descriptor, if any.
+ *
+ * @param transport the transport
+ * @param fd_p pointer to fill in with the descriptor
+ * @returns #TRUE if a descriptor was available
+ */
+dbus_bool_t
+_dbus_transport_get_unix_fd (DBusTransport *transport,
+                             int           *fd_p)
+{
+  dbus_bool_t retval;
+  
+  if (transport->vtable->get_unix_fd == NULL)
+    return FALSE;
+
+  if (transport->disconnected)
+    return FALSE;
+
+  _dbus_transport_ref (transport);
+
+  retval = (* transport->vtable->get_unix_fd) (transport,
+                                               fd_p);
+  
+  _dbus_transport_unref (transport);
+
+  return retval;
+}
+
+/**
  * Performs a single poll()/select() on the transport's file
  * descriptors and then reads/writes data as appropriate,
  * queueing incoming messages and sending outgoing messages.
index b6c7a4e..88193f3 100644 (file)
@@ -59,6 +59,9 @@ void               _dbus_transport_set_max_received_size  (DBusTransport
 long               _dbus_transport_get_max_received_size  (DBusTransport              *transport);
 dbus_bool_t        _dbus_transport_get_unix_user          (DBusTransport              *transport,
                                                            unsigned long              *uid);
+dbus_bool_t        _dbus_transport_get_unix_fd            (DBusTransport              *transport,
+                                                           int                        *fd_p);
+
 dbus_bool_t        _dbus_transport_get_unix_process_id     (DBusTransport              *transport,
                                                            unsigned long              *pid);
 void               _dbus_transport_set_unix_user_function (DBusTransport              *transport,
index 4500ad7..73bd02a 100644 (file)
   <limit name="max_connections_per_user">64</limit>
   <limit name="max_pending_activations">64</limit>
   <limit name="max_services_per_connection">256</limit>
-                                   
+
+  <selinux>
+        <associate own="org.freedesktop.FrobationaryMeasures"
+         context="my_selinux_context_t"/>
+        <associate own="org.freedesktop.BlahBlahBlah"
+         context="foo_t"/>
+  </selinux>
+
 </busconfig>