diff options
| author | Havoc Pennington <hp@redhat.com> | 2004-07-30 05:59:34 +0000 | 
|---|---|---|
| committer | Havoc Pennington <hp@redhat.com> | 2004-07-30 05:59:34 +0000 | 
| commit | 1e9b185b0c274ef0d684b1e43418388225321e72 (patch) | |
| tree | 66bb08beb9ea1b4250953294134e2c995f8adf34 | |
| parent | 4076d31c71bee332c4a697597a93345b45850b33 (diff) | |
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
| -rw-r--r-- | ChangeLog | 31 | ||||
| -rw-r--r-- | bus/Makefile.am | 4 | ||||
| -rw-r--r-- | bus/bus.c | 49 | ||||
| -rw-r--r-- | bus/bus.h | 5 | ||||
| -rw-r--r-- | bus/config-parser.c | 129 | ||||
| -rw-r--r-- | bus/config-parser.h | 3 | ||||
| -rw-r--r-- | bus/connection.c | 39 | ||||
| -rw-r--r-- | bus/connection.h | 3 | ||||
| -rw-r--r-- | bus/dbus-daemon-1.1.in | 104 | ||||
| -rw-r--r-- | bus/main.c | 10 | ||||
| -rw-r--r-- | bus/policy.c | 11 | ||||
| -rw-r--r-- | bus/selinux.c | 658 | ||||
| -rw-r--r-- | bus/selinux.h | 60 | ||||
| -rw-r--r-- | bus/services.c | 42 | ||||
| -rw-r--r-- | bus/services.h | 3 | ||||
| -rw-r--r-- | bus/test-main.c | 6 | ||||
| -rw-r--r-- | configure.in | 40 | ||||
| -rw-r--r-- | dbus/dbus-connection.c | 31 | ||||
| -rw-r--r-- | dbus/dbus-connection.h | 3 | ||||
| -rw-r--r-- | dbus/dbus-transport-protected.h | 5 | ||||
| -rw-r--r-- | dbus/dbus-transport-unix.c | 15 | ||||
| -rw-r--r-- | dbus/dbus-transport.c | 29 | ||||
| -rw-r--r-- | dbus/dbus-transport.h | 3 | ||||
| -rw-r--r-- | test/data/valid-config-files/basic.conf | 9 | 
24 files changed, 1266 insertions, 26 deletions
@@ -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 diff --git a/bus/Makefile.am b/bus/Makefile.am index a6c4b17b..a9761be5 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -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				\ @@ -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); @@ -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 */ diff --git a/bus/config-parser.c b/bus/config-parser.c index faa5b55b..29fade16 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -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; diff --git a/bus/config-parser.h b/bus/config-parser.h index a48eda19..77b92551 100644 --- a/bus/config-parser.h +++ b/bus/config-parser.h @@ -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.   */ diff --git a/bus/connection.c b/bus/connection.c index 6b4fbe73..b751cca8 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -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.   * diff --git a/bus/connection.h b/bus/connection.h index 1d93957b..ee1291fa 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -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); diff --git a/bus/dbus-daemon-1.1.in b/bus/dbus-daemon-1.1.in index 390d145c..893e2413 100644 --- a/bus/dbus-daemon-1.1.in +++ b/bus/dbus-daemon-1.1.in @@ -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 @@ -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;  } diff --git a/bus/policy.c b/bus/policy.c index 3c7bba9b..c7359c8d 100644 --- a/bus/policy.c +++ b/bus/policy.c @@ -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 index 00000000..24650ed5 --- /dev/null +++ b/bus/selinux.c @@ -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 index 00000000..79a9f98b --- /dev/null +++ b/bus/selinux.h @@ -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 */ diff --git a/bus/services.c b/bus/services.c index 33caded6..81e3eaf3 100644 --- a/bus/services.c +++ b/bus/services.c @@ -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) diff --git a/bus/services.h b/bus/services.h index fae324d7..fa7a3a11 100644 --- a/bus/services.h +++ b/bus/services.h @@ -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); diff --git a/bus/test-main.c b/bus/test-main.c index 1626f2d7..6d453b40 100644 --- a/bus/test-main.c +++ b/bus/test-main.c @@ -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 */ diff --git a/configure.in b/configure.in index 2c18dd45..67f971c6 100644 --- a/configure.in +++ b/configure.in @@ -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} diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 58ab7900..91a2100e 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -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. diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index 12de0c05..c8c66a39 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -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 */ diff --git a/dbus/dbus-transport-protected.h b/dbus/dbus-transport-protected.h index 409e683b..4a9ce96d 100644 --- a/dbus/dbus-transport-protected.h +++ b/dbus/dbus-transport-protected.h @@ -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. */ diff --git a/dbus/dbus-transport-unix.c b/dbus/dbus-transport-unix.c index 37825f1c..3447ae1d 100644 --- a/dbus/dbus-transport-unix.c +++ b/dbus/dbus-transport-unix.c @@ -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  };  /** diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index ada960d4..dde1c6d2 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -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. diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index b6c7a4ec..88193f38 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -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, diff --git a/test/data/valid-config-files/basic.conf b/test/data/valid-config-files/basic.conf index 4500ad70..73bd02ac 100644 --- a/test/data/valid-config-files/basic.conf +++ b/test/data/valid-config-files/basic.conf @@ -21,5 +21,12 @@    <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>  | 
