summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2004-07-30 05:59:34 +0000
committerHavoc Pennington <hp@redhat.com>2004-07-30 05:59:34 +0000
commit1e9b185b0c274ef0d684b1e43418388225321e72 (patch)
tree66bb08beb9ea1b4250953294134e2c995f8adf34
parent4076d31c71bee332c4a697597a93345b45850b33 (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--ChangeLog31
-rw-r--r--bus/Makefile.am4
-rw-r--r--bus/bus.c49
-rw-r--r--bus/bus.h5
-rw-r--r--bus/config-parser.c129
-rw-r--r--bus/config-parser.h3
-rw-r--r--bus/connection.c39
-rw-r--r--bus/connection.h3
-rw-r--r--bus/dbus-daemon-1.1.in104
-rw-r--r--bus/main.c10
-rw-r--r--bus/policy.c11
-rw-r--r--bus/selinux.c658
-rw-r--r--bus/selinux.h60
-rw-r--r--bus/services.c42
-rw-r--r--bus/services.h3
-rw-r--r--bus/test-main.c6
-rw-r--r--configure.in40
-rw-r--r--dbus/dbus-connection.c31
-rw-r--r--dbus/dbus-connection.h3
-rw-r--r--dbus/dbus-transport-protected.h5
-rw-r--r--dbus/dbus-transport-unix.c15
-rw-r--r--dbus/dbus-transport.c29
-rw-r--r--dbus/dbus-transport.h3
-rw-r--r--test/data/valid-config-files/basic.conf9
24 files changed, 1266 insertions, 26 deletions
diff --git a/ChangeLog b/ChangeLog
index 88afd037..90fa3f40 100644
--- 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
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 \
diff --git a/bus/bus.c b/bus/bus.c
index f3ef761b..61f6d7d3 100644
--- 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);
diff --git a/bus/bus.h b/bus/bus.h
index 7d05508b..eab79347 100644
--- 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 */
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
diff --git a/bus/main.c b/bus/main.c
index 8f175320..32a6161b 100644
--- a/bus/main.c
+++ b/bus/main.c
@@ -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>