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> |