summaryrefslogtreecommitdiff
path: root/bus
diff options
context:
space:
mode:
Diffstat (limited to 'bus')
-rw-r--r--bus/Makefile.am4
-rw-r--r--bus/Makefile.in49
-rw-r--r--bus/apparmor.c1128
-rw-r--r--bus/apparmor.h66
-rw-r--r--bus/bus.c90
-rw-r--r--bus/bus.h1
-rw-r--r--bus/config-parser-common.c6
-rw-r--r--bus/config-parser-common.h3
-rw-r--r--bus/config-parser.c24
-rw-r--r--bus/connection.c35
-rw-r--r--bus/connection.h1
-rw-r--r--bus/driver.c136
-rw-r--r--bus/main.c8
-rw-r--r--bus/services.c7
-rw-r--r--bus/session.conf.in3
-rw-r--r--bus/signals.c9
-rw-r--r--bus/signals.h2
-rw-r--r--bus/system.conf.in3
18 files changed, 1528 insertions, 47 deletions
diff --git a/bus/Makefile.am b/bus/Makefile.am
index c0bc1549..9d3cb006 100644
--- a/bus/Makefile.am
+++ b/bus/Makefile.am
@@ -4,6 +4,7 @@ dbus_daemon_execdir = $(DBUS_DAEMONDIR)
DBUS_BUS_LIBS = \
$(XML_LIBS) \
$(SELINUX_LIBS) \
+ $(APPARMOR_LIBS) \
$(THREAD_LIBS) \
$(ADT_LIBS) \
$(NETWORK_libs) \
@@ -18,6 +19,7 @@ DBUS_LAUNCHER_LIBS = \
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(XML_CFLAGS) \
+ $(APPARMOR_CFLAGS) \
-DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \
-DDBUS_COMPILATION \
-DDBUS_STATIC_BUILD \
@@ -69,6 +71,8 @@ BUS_SOURCES= \
activation.c \
activation.h \
activation-exit-codes.h \
+ apparmor.c \
+ apparmor.h \
bus.c \
bus.h \
config-parser.c \
diff --git a/bus/Makefile.in b/bus/Makefile.in
index 6bbcecd6..f162f171 100644
--- a/bus/Makefile.in
+++ b/bus/Makefile.in
@@ -136,21 +136,21 @@ am__installdirs = "$(DESTDIR)$(dbus_daemon_execdir)" \
PROGRAMS = $(dbus_daemon_exec_PROGRAMS) $(libexec_PROGRAMS) \
$(noinst_PROGRAMS)
am__dbus_daemon_SOURCES_DIST = activation.c activation.h \
- activation-exit-codes.h bus.c bus.h config-parser.c \
- config-parser.h config-parser-common.c config-parser-common.h \
- connection.c connection.h desktop-file.c desktop-file.h \
- dir-watch-default.c dir-watch-inotify.c dir-watch-kqueue.c \
- dir-watch.h dispatch.c dispatch.h driver.c driver.h \
- expirelist.c expirelist.h policy.c policy.h selinux.h \
- selinux.c services.c services.h signals.c signals.h stats.c \
- stats.h test.c test.h utils.c utils.h config-loader-expat.c \
- main.c
+ activation-exit-codes.h apparmor.c apparmor.h bus.c bus.h \
+ config-parser.c config-parser.h config-parser-common.c \
+ config-parser-common.h connection.c connection.h \
+ desktop-file.c desktop-file.h dir-watch-default.c \
+ dir-watch-inotify.c dir-watch-kqueue.c dir-watch.h dispatch.c \
+ dispatch.h driver.c driver.h expirelist.c expirelist.h \
+ policy.c policy.h selinux.h selinux.c services.c services.h \
+ signals.c signals.h stats.c stats.h test.c test.h utils.c \
+ utils.h config-loader-expat.c main.c
@DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-default.$(OBJEXT)
@DBUS_BUS_ENABLE_INOTIFY_TRUE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-inotify.$(OBJEXT)
@DBUS_BUS_ENABLE_KQUEUE_TRUE@am__objects_1 = \
@DBUS_BUS_ENABLE_KQUEUE_TRUE@ dir-watch-kqueue.$(OBJEXT)
am__objects_2 = config-loader-expat.$(OBJEXT)
-am__objects_3 = activation.$(OBJEXT) bus.$(OBJEXT) \
+am__objects_3 = activation.$(OBJEXT) apparmor.$(OBJEXT) bus.$(OBJEXT) \
config-parser.$(OBJEXT) config-parser-common.$(OBJEXT) \
connection.$(OBJEXT) desktop-file.$(OBJEXT) $(am__objects_1) \
dispatch.$(OBJEXT) driver.$(OBJEXT) expirelist.$(OBJEXT) \
@@ -162,7 +162,7 @@ dbus_daemon_OBJECTS = $(am_dbus_daemon_OBJECTS)
am__DEPENDENCIES_1 =
am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
dbus_daemon_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -195,15 +195,15 @@ dbus_daemon_launch_helper_test_OBJECTS = \
dbus_daemon_launch_helper_test_DEPENDENCIES = \
$(top_builddir)/dbus/libdbus-internal.la $(am__DEPENDENCIES_3)
am__test_bus_SOURCES_DIST = activation.c activation.h \
- activation-exit-codes.h bus.c bus.h config-parser.c \
- config-parser.h config-parser-common.c config-parser-common.h \
- connection.c connection.h desktop-file.c desktop-file.h \
- dir-watch-default.c dir-watch-inotify.c dir-watch-kqueue.c \
- dir-watch.h dispatch.c dispatch.h driver.c driver.h \
- expirelist.c expirelist.h policy.c policy.h selinux.h \
- selinux.c services.c services.h signals.c signals.h stats.c \
- stats.h test.c test.h utils.c utils.h config-loader-expat.c \
- test-main.c
+ activation-exit-codes.h apparmor.c apparmor.h bus.c bus.h \
+ config-parser.c config-parser.h config-parser-common.c \
+ config-parser-common.h connection.c connection.h \
+ desktop-file.c desktop-file.h dir-watch-default.c \
+ dir-watch-inotify.c dir-watch-kqueue.c dir-watch.h dispatch.c \
+ dispatch.h driver.c driver.h expirelist.c expirelist.h \
+ policy.c policy.h selinux.h selinux.c services.c services.h \
+ signals.c signals.h stats.c stats.h test.c test.h utils.c \
+ utils.h config-loader-expat.c test-main.c
am_test_bus_OBJECTS = $(am__objects_3) test-main.$(OBJEXT)
test_bus_OBJECTS = $(am_test_bus_OBJECTS)
test_bus_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la \
@@ -328,6 +328,8 @@ ACLOCAL = @ACLOCAL@
ADT_LIBS = @ADT_LIBS@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPARMOR_CFLAGS = @APPARMOR_CFLAGS@
+APPARMOR_LIBS = @APPARMOR_LIBS@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
@@ -382,6 +384,7 @@ DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DOXYGEN = @DOXYGEN@
DSYMUTIL = @DSYMUTIL@
+DUCKTYPE = @DUCKTYPE@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
@@ -471,6 +474,7 @@ X_CFLAGS = @X_CFLAGS@
X_EXTRA_LIBS = @X_EXTRA_LIBS@
X_LIBS = @X_LIBS@
X_PRE_LIBS = @X_PRE_LIBS@
+YELP_BUILD = @YELP_BUILD@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
@@ -535,6 +539,7 @@ dbus_daemon_execdir = $(DBUS_DAEMONDIR)
DBUS_BUS_LIBS = \
$(XML_LIBS) \
$(SELINUX_LIBS) \
+ $(APPARMOR_LIBS) \
$(THREAD_LIBS) \
$(ADT_LIBS) \
$(NETWORK_libs) \
@@ -549,6 +554,7 @@ DBUS_LAUNCHER_LIBS = \
AM_CPPFLAGS = \
-I$(top_srcdir) \
$(XML_CFLAGS) \
+ $(APPARMOR_CFLAGS) \
-DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \
-DDBUS_COMPILATION \
-DDBUS_STATIC_BUILD \
@@ -583,6 +589,8 @@ BUS_SOURCES = \
activation.c \
activation.h \
activation-exit-codes.h \
+ apparmor.c \
+ apparmor.h \
bus.c \
bus.h \
config-parser.c \
@@ -976,6 +984,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper-bin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apparmor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-loader-expat.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser-common.Po@am__quote@
diff --git a/bus/apparmor.c b/bus/apparmor.c
new file mode 100644
index 00000000..a1b3621a
--- /dev/null
+++ b/bus/apparmor.c
@@ -0,0 +1,1128 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ * apparmor.c AppArmor security checks for D-Bus
+ *
+ * Based on selinux.c
+ *
+ * Copyright © 2014-2015 Canonical, Ltd.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "apparmor.h"
+
+#ifdef HAVE_APPARMOR
+
+#include <dbus/dbus-internals.h>
+#include <dbus/dbus-string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/apparmor.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#ifdef HAVE_LIBAUDIT
+#include <cap-ng.h>
+#include <libaudit.h>
+#endif /* HAVE_LIBAUDIT */
+
+#include "connection.h"
+#include "utils.h"
+
+/* Store the value telling us if AppArmor D-Bus mediation is enabled. */
+static dbus_bool_t apparmor_enabled = FALSE;
+
+typedef enum {
+ APPARMOR_DISABLED,
+ APPARMOR_ENABLED,
+ APPARMOR_REQUIRED
+} AppArmorConfigMode;
+
+/* Store the value of the AppArmor mediation mode in the bus configuration */
+static AppArmorConfigMode apparmor_config_mode = APPARMOR_ENABLED;
+
+#ifdef HAVE_LIBAUDIT
+static int audit_fd = -1;
+#endif
+
+/* The AppArmor context, consisting of a label and a mode. */
+struct BusAppArmorConfinement
+{
+ int refcount; /* Reference count */
+
+ char *label; /* AppArmor confinement label */
+ const char *mode; /* AppArmor confinement mode (freed by freeing *label) */
+};
+
+static BusAppArmorConfinement *bus_con = NULL;
+
+/**
+ * Callers of this function give up ownership of the *label and *mode
+ * pointers.
+ *
+ * Additionally, the responsibility of freeing *label and *mode becomes the
+ * responsibility of the bus_apparmor_confinement_unref() function. However, it
+ * does not free *mode because libapparmor's aa_getcon(), and libapparmor's
+ * other related functions, allocate a single buffer for *label and *mode and
+ * then separate the two char arrays with a NUL char. See the aa_getcon(2) man
+ * page for more details.
+ */
+static BusAppArmorConfinement*
+bus_apparmor_confinement_new (char *label,
+ const char *mode)
+{
+ BusAppArmorConfinement *confinement;
+
+ confinement = dbus_new0 (BusAppArmorConfinement, 1);
+ if (confinement != NULL)
+ {
+ confinement->refcount = 1;
+ confinement->label = label;
+ confinement->mode = mode;
+ }
+
+ return confinement;
+}
+
+void
+bus_apparmor_audit_init (void)
+{
+#ifdef HAVE_LIBAUDIT
+ audit_fd = audit_open ();
+
+ if (audit_fd < 0)
+ {
+ /* If kernel doesn't support audit, bail out */
+ if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT)
+ return;
+ /* If user bus, bail out */
+ if (errno == EPERM && getuid () != 0)
+ return;
+ _dbus_warn ("Failed opening connection to the audit subsystem");
+ }
+#endif /* HAVE_LIBAUDIT */
+}
+
+/*
+ * Return TRUE on successful check, FALSE on OOM.
+ * Set *is_supported to whether AA has D-Bus features.
+ */
+static dbus_bool_t
+_bus_apparmor_detect_aa_dbus_support (dbus_bool_t *is_supported)
+{
+ int mask_file;
+ DBusString aa_dbus;
+ char *aa_securityfs = NULL;
+ dbus_bool_t retval = FALSE;
+
+ *is_supported = FALSE;
+
+ if (!_dbus_string_init (&aa_dbus))
+ return FALSE;
+
+ if (aa_find_mountpoint (&aa_securityfs) != 0)
+ goto out;
+
+ /*
+ * John Johansen has confirmed that the mainline kernel will not have
+ * the apparmorfs/features/dbus/mask file until the mainline kernel
+ * has AppArmor getpeersec support.
+ */
+ if (!_dbus_string_append (&aa_dbus, aa_securityfs) ||
+ !_dbus_string_append (&aa_dbus, "/features/dbus/mask"))
+ goto out;
+
+ /* We need to open() the flag file, not just stat() it, because AppArmor
+ * does not mediate stat() in the apparmorfs. If you have a
+ * dbus-daemon inside an LXC container, with insufficiently broad
+ * AppArmor privileges to do its own AppArmor mediation, the desired
+ * result is that it behaves as if AppArmor was not present; but a stat()
+ * here would succeed, and result in it trying and failing to do full
+ * mediation. https://bugs.launchpad.net/ubuntu/+source/dbus/+bug/1238267 */
+ mask_file = open (_dbus_string_get_const_data (&aa_dbus),
+ O_RDONLY | O_CLOEXEC);
+ if (mask_file != -1)
+ {
+ *is_supported = TRUE;
+ close (mask_file);
+ }
+
+ retval = TRUE;
+
+out:
+ free (aa_securityfs);
+ _dbus_string_free (&aa_dbus);
+
+ return retval;
+}
+
+static dbus_bool_t
+modestr_is_complain (const char *mode)
+{
+ if (mode && strcmp (mode, "complain") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+static void
+log_message (dbus_bool_t allow, const char *op, DBusString *data)
+{
+ const char *mstr;
+
+ if (allow)
+ mstr = "ALLOWED";
+ else
+ mstr = "DENIED";
+
+#ifdef HAVE_LIBAUDIT
+ if (audit_fd >= 0)
+ {
+ DBusString avc;
+
+ capng_get_caps_process ();
+ if (!capng_have_capability (CAPNG_EFFECTIVE, CAP_AUDIT_WRITE))
+ goto syslog;
+
+ if (!_dbus_string_init (&avc))
+ goto syslog;
+
+ if (!_dbus_string_append_printf (&avc,
+ "apparmor=\"%s\" operation=\"dbus_%s\" %s\n",
+ mstr, op, _dbus_string_get_const_data (data)))
+ {
+ _dbus_string_free (&avc);
+ goto syslog;
+ }
+
+ /* FIXME: need to change this to show real user */
+ audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC,
+ _dbus_string_get_const_data (&avc),
+ NULL, NULL, NULL, getuid ());
+ _dbus_string_free (&avc);
+ return;
+ }
+
+syslog:
+#endif /* HAVE_LIBAUDIT */
+
+ syslog (LOG_USER | LOG_NOTICE, "apparmor=\"%s\" operation=\"dbus_%s\" %s\n",
+ mstr, op, _dbus_string_get_const_data (data));
+}
+
+static dbus_bool_t
+_dbus_append_pair_uint (DBusString *auxdata, const char *name,
+ unsigned long value)
+{
+ return _dbus_string_append (auxdata, " ") &&
+ _dbus_string_append (auxdata, name) &&
+ _dbus_string_append (auxdata, "=") &&
+ _dbus_string_append_uint (auxdata, value);
+}
+
+static dbus_bool_t
+_dbus_append_pair_str (DBusString *auxdata, const char *name, const char *value)
+{
+ return _dbus_string_append (auxdata, " ") &&
+ _dbus_string_append (auxdata, name) &&
+ _dbus_string_append (auxdata, "=\"") &&
+ _dbus_string_append (auxdata, value) &&
+ _dbus_string_append (auxdata, "\"");
+}
+
+static dbus_bool_t
+_dbus_append_mask (DBusString *auxdata, uint32_t mask)
+{
+ const char *mask_str;
+
+ /* Only one permission bit can be set */
+ if (mask == AA_DBUS_SEND)
+ mask_str = "send";
+ else if (mask == AA_DBUS_RECEIVE)
+ mask_str = "receive";
+ else if (mask == AA_DBUS_BIND)
+ mask_str = "bind";
+ else
+ return FALSE;
+
+ return _dbus_append_pair_str (auxdata, "mask", mask_str);
+}
+
+static dbus_bool_t
+is_unconfined (const char *con, const char *mode)
+{
+ /* treat con == NULL as confined as it is going to result in a denial */
+ if ((!mode && con && strcmp (con, "unconfined") == 0) ||
+ strcmp (mode, "unconfined") == 0)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static dbus_bool_t
+query_append (DBusString *query, const char *buffer)
+{
+ if (!_dbus_string_append_byte (query, '\0'))
+ return FALSE;
+
+ if (buffer && !_dbus_string_append (query, buffer))
+ return FALSE;
+
+ return TRUE;
+}
+
+static dbus_bool_t
+build_common_query (DBusString *query, const char *con, const char *bustype)
+{
+ /**
+ * libapparmor's aa_query_label() function scribbles over the first
+ * AA_QUERY_CMD_LABEL_SIZE bytes of the query string with a private value.
+ */
+ return _dbus_string_insert_bytes (query, 0, AA_QUERY_CMD_LABEL_SIZE, 0) &&
+ _dbus_string_append (query, con) &&
+ _dbus_string_append_byte (query, '\0') &&
+ _dbus_string_append_byte (query, AA_CLASS_DBUS) &&
+ _dbus_string_append (query, bustype ? bustype : "");
+}
+
+static dbus_bool_t
+build_service_query (DBusString *query,
+ const char *con,
+ const char *bustype,
+ const char *name)
+{
+ return build_common_query (query, con, bustype) &&
+ query_append (query, name);
+}
+
+static dbus_bool_t
+build_message_query (DBusString *query,
+ const char *src_con,
+ const char *bustype,
+ const char *name,
+ const char *dst_con,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ return build_common_query (query, src_con, bustype) &&
+ query_append (query, name) &&
+ query_append (query, dst_con) &&
+ query_append (query, path) &&
+ query_append (query, interface) &&
+ query_append (query, member);
+}
+
+static dbus_bool_t
+build_eavesdrop_query (DBusString *query, const char *con, const char *bustype)
+{
+ return build_common_query (query, con, bustype);
+}
+
+static void
+set_error_from_query_errno (DBusError *error, int error_number)
+{
+ dbus_set_error (error, _dbus_error_from_errno (error_number),
+ "Failed to query AppArmor policy: %s",
+ _dbus_strerror (error_number));
+}
+
+static void
+set_error_from_denied_message (DBusError *error,
+ DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ dbus_bool_t requested_reply,
+ const char *msgtype,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ const char *destination)
+{
+ const char *proposed_recipient_loginfo;
+ const char *unset = "(unset)";
+
+ proposed_recipient_loginfo = proposed_recipient ?
+ bus_connection_get_loginfo (proposed_recipient) :
+ "bus";
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "An AppArmor policy prevents this sender from sending this "
+ "message to this recipient; type=\"%s\", "
+ "sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" "
+ "error name=\"%s\" requested_reply=\"%d\" "
+ "destination=\"%s\" (%s)",
+ msgtype,
+ bus_connection_get_name (sender),
+ bus_connection_get_loginfo (sender),
+ interface ? interface : unset,
+ member ? member : unset,
+ error_name ? error_name : unset,
+ requested_reply,
+ destination,
+ proposed_recipient_loginfo);
+}
+#endif /* HAVE_APPARMOR */
+
+/**
+ * Do early initialization; determine whether AppArmor is enabled.
+ * Return TRUE on successful check (whether AppArmor is actually
+ * enabled or not) or FALSE on OOM.
+ */
+dbus_bool_t
+bus_apparmor_pre_init (void)
+{
+#ifdef HAVE_APPARMOR
+ apparmor_enabled = FALSE;
+
+ if (!aa_is_enabled ())
+ return TRUE;
+
+ if (!_bus_apparmor_detect_aa_dbus_support (&apparmor_enabled))
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+dbus_bool_t
+bus_apparmor_set_mode_from_config (const char *mode, DBusError *error)
+{
+#ifdef HAVE_APPARMOR
+ if (mode != NULL)
+ {
+ if (strcmp (mode, "disabled") == 0)
+ apparmor_config_mode = APPARMOR_DISABLED;
+ else if (strcmp (mode, "enabled") == 0)
+ apparmor_config_mode = APPARMOR_ENABLED;
+ else if (strcmp (mode, "required") == 0)
+ apparmor_config_mode = APPARMOR_REQUIRED;
+ else
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Mode attribute on <apparmor> must have value "
+ "\"required\", \"enabled\" or \"disabled\", "
+ "not \"%s\"", mode);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+#else
+ if (mode == NULL || strcmp (mode, "disabled") == 0 ||
+ strcmp (mode, "enabled") == 0)
+ return TRUE;
+
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Mode attribute on <apparmor> must have value \"enabled\" or "
+ "\"disabled\" but cannot be \"%s\" when D-Bus is built "
+ "without AppArmor support", mode);
+ return FALSE;
+#endif
+}
+
+/**
+ * Verify that the config mode is compatible with the kernel's AppArmor
+ * support. If AppArmor mediation will be enabled, determine the bus
+ * confinement label.
+ */
+dbus_bool_t
+bus_apparmor_full_init (DBusError *error)
+{
+#ifdef HAVE_APPARMOR
+ char *label, *mode;
+
+ if (apparmor_enabled)
+ {
+ if (apparmor_config_mode == APPARMOR_DISABLED)
+ {
+ apparmor_enabled = FALSE;
+ return TRUE;
+ }
+
+ if (bus_con == NULL)
+ {
+ if (aa_getcon (&label, &mode) == -1)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Error getting AppArmor context of bus: %s",
+ _dbus_strerror (errno));
+ return FALSE;
+ }
+
+ bus_con = bus_apparmor_confinement_new (label, mode);
+ if (bus_con == NULL)
+ {
+ BUS_SET_OOM (error);
+ free (label);
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ if (apparmor_config_mode == APPARMOR_REQUIRED)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "AppArmor mediation required but not present");
+ return FALSE;
+ }
+ else if (apparmor_config_mode == APPARMOR_ENABLED)
+ {
+ return TRUE;
+ }
+ }
+#endif
+
+ return TRUE;
+}
+
+void
+bus_apparmor_shutdown (void)
+{
+#ifdef HAVE_APPARMOR
+ if (!apparmor_enabled)
+ return;
+
+ _dbus_verbose ("AppArmor shutdown\n");
+
+ bus_apparmor_confinement_unref (bus_con);
+ bus_con = NULL;
+
+#ifdef HAVE_LIBAUDIT
+ audit_close (audit_fd);
+#endif /* HAVE_LIBAUDIT */
+
+#endif /* HAVE_APPARMOR */
+}
+
+dbus_bool_t
+bus_apparmor_enabled (void)
+{
+#ifdef HAVE_APPARMOR
+ return apparmor_enabled;
+#else
+ return FALSE;
+#endif
+}
+
+void
+bus_apparmor_confinement_unref (BusAppArmorConfinement *confinement)
+{
+#ifdef HAVE_APPARMOR
+ if (!apparmor_enabled)
+ return;
+
+ _dbus_assert (confinement != NULL);
+ _dbus_assert (confinement->refcount > 0);
+
+ confinement->refcount -= 1;
+
+ if (confinement->refcount == 0)
+ {
+ /**
+ * Do not free confinement->mode, as libapparmor does a single malloc for
+ * both confinement->label and confinement->mode.
+ */
+ free (confinement->label);
+ dbus_free (confinement);
+ }
+#endif
+}
+
+void
+bus_apparmor_confinement_ref (BusAppArmorConfinement *confinement)
+{
+#ifdef HAVE_APPARMOR
+ if (!apparmor_enabled)
+ return;
+
+ _dbus_assert (confinement != NULL);
+ _dbus_assert (confinement->refcount > 0);
+
+ confinement->refcount += 1;
+#endif /* HAVE_APPARMOR */
+}
+
+BusAppArmorConfinement*
+bus_apparmor_init_connection_confinement (DBusConnection *connection,
+ DBusError *error)
+{
+#ifdef HAVE_APPARMOR
+ BusAppArmorConfinement *confinement;
+ char *label, *mode;
+ int fd;
+
+ if (!apparmor_enabled)
+ return NULL;
+
+ _dbus_assert (connection != NULL);
+
+ if (!dbus_connection_get_socket (connection, &fd))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Failed to get socket file descriptor of connection");
+ return NULL;
+ }
+
+ if (aa_getpeercon (fd, &label, &mode) == -1)
+ {
+ if (errno == ENOMEM)
+ BUS_SET_OOM (error);
+ else
+ dbus_set_error (error, _dbus_error_from_errno (errno),
+ "Failed to get AppArmor confinement information of socket peer: %s",
+ _dbus_strerror (errno));
+ return NULL;
+ }
+
+ confinement = bus_apparmor_confinement_new (label, mode);
+ if (confinement == NULL)
+ {
+ BUS_SET_OOM (error);
+ free (label);
+ return NULL;
+ }
+
+ return confinement;
+#else
+ return NULL;
+#endif /* HAVE_APPARMOR */
+}
+
+/**
+ * Returns true if the given connection can acquire a service,
+ * using the tasks security context
+ *
+ * @param connection connection that wants to own the service
+ * @param bustype name of the bus
+ * @param service_name the name of the service to acquire
+ * @param error the reason for failure when FALSE is returned
+ * @returns TRUE if acquire is permitted
+ */
+dbus_bool_t
+bus_apparmor_allows_acquire_service (DBusConnection *connection,
+ const char *bustype,
+ const char *service_name,
+ DBusError *error)
+{
+
+#ifdef HAVE_APPARMOR
+ BusAppArmorConfinement *con = NULL;
+ DBusString qstr, auxdata;
+ dbus_bool_t free_auxdata = FALSE;
+ dbus_bool_t allow = FALSE, audit = TRUE;
+ unsigned long pid;
+ int res, serrno = 0;
+
+ if (!apparmor_enabled)
+ return TRUE;
+
+ _dbus_assert (connection != NULL);
+
+ con = bus_connection_dup_apparmor_confinement (connection);
+
+ if (is_unconfined (con->label, con->mode))
+ {
+ allow = TRUE;
+ audit = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_string_init (&qstr))
+ goto oom;
+
+ if (!build_service_query (&qstr, con->label, bustype, service_name))
+ {
+ _dbus_string_free (&qstr);
+ goto oom;
+ }
+
+ res = aa_query_label (AA_DBUS_BIND,
+ _dbus_string_get_data (&qstr),
+ _dbus_string_get_length (&qstr),
+ &allow, &audit);
+ _dbus_string_free (&qstr);
+ if (res == -1)
+ {
+ serrno = errno;
+ set_error_from_query_errno (error, serrno);
+ goto audit;
+ }
+
+ /* Don't fail operations on profiles in complain mode */
+ if (modestr_is_complain (con->mode))
+ allow = TRUE;
+
+ if (!allow)
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to own the service "
+ "\"%s\" due to AppArmor policy",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) : "(inactive)",
+ service_name);
+
+ if (!audit)
+ goto out;
+
+ audit:
+ if (!_dbus_string_init (&auxdata))
+ goto oom;
+ free_auxdata = TRUE;
+
+ if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown"))
+ goto oom;
+
+ if (!_dbus_append_pair_str (&auxdata, "name", service_name))
+ goto oom;
+
+ if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno)))
+ goto oom;
+
+ if (!_dbus_append_mask (&auxdata, AA_DBUS_BIND))
+ goto oom;
+
+ if (connection && dbus_connection_get_unix_process_id (connection, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "pid", pid))
+ goto oom;
+
+ if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label))
+ goto oom;
+
+ log_message (allow, "bind", &auxdata);
+
+ out:
+ if (con != NULL)
+ bus_apparmor_confinement_unref (con);
+ if (free_auxdata)
+ _dbus_string_free (&auxdata);
+ return allow;
+
+ oom:
+ if (error != NULL && !dbus_error_is_set (error))
+ BUS_SET_OOM (error);
+ allow = FALSE;
+ goto out;
+
+#else
+ return TRUE;
+#endif /* HAVE_APPARMOR */
+}
+
+/**
+ * Check if Apparmor 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.
+ * @param requested_reply TRUE if the message is a reply requested by
+ * proposed_recipient
+ * @param bustype name of the bus
+ * @param msgtype message type (DBUS_MESSAGE_TYPE_METHOD_CALL, etc.)
+ * @param path object path the message should be sent to
+ * @param interface the type of the object instance
+ * @param member the member of the object
+ * @param error_name the name of the error if the message type is error
+ * @param destination name that the message should be sent to
+ * @param source name that the message should be sent from
+ * @param error the reason for failure when FALSE is returned
+ * @returns TRUE if the message is permitted
+ */
+dbus_bool_t
+bus_apparmor_allows_send (DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ dbus_bool_t requested_reply,
+ const char *bustype,
+ int msgtype,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ const char *destination,
+ const char *source,
+ DBusError *error)
+{
+#ifdef HAVE_APPARMOR
+ BusAppArmorConfinement *src_con = NULL, *dst_con = NULL;
+ DBusString qstr, auxdata;
+ dbus_bool_t src_allow = FALSE, dst_allow = FALSE;
+ dbus_bool_t src_audit = TRUE, dst_audit = TRUE;
+ dbus_bool_t free_auxdata = FALSE;
+ unsigned long pid;
+ int len, res, src_errno = 0, dst_errno = 0;
+ uint32_t src_perm = AA_DBUS_SEND, dst_perm = AA_DBUS_RECEIVE;
+ const char *msgtypestr = dbus_message_type_to_string(msgtype);
+
+ if (!apparmor_enabled)
+ return TRUE;
+
+ _dbus_assert (sender != NULL);
+
+ src_con = bus_connection_dup_apparmor_confinement (sender);
+
+ if (proposed_recipient)
+ {
+ dst_con = bus_connection_dup_apparmor_confinement (proposed_recipient);
+ }
+ else
+ {
+ dst_con = bus_con;
+ bus_apparmor_confinement_ref (dst_con);
+ }
+
+ /* map reply messages to initial send and receive permission. That is
+ * permission to receive a message from X grants permission to reply to X.
+ * And permission to send a message to Y grants permission to receive a reply
+ * from Y. Note that this only applies to requested replies. Unrequested
+ * replies still require a policy query.
+ */
+ if (requested_reply)
+ {
+ /* ignore requested reply messages and let dbus reply mapping handle them
+ * as the send was already allowed
+ */
+ src_allow = TRUE;
+ dst_allow = TRUE;
+ goto out;
+ }
+
+ if (is_unconfined (src_con->label, src_con->mode))
+ {
+ src_allow = TRUE;
+ src_audit = FALSE;
+ }
+ else
+ {
+ if (!_dbus_string_init (&qstr))
+ goto oom;
+
+ if (!build_message_query (&qstr, src_con->label, bustype, destination,
+ dst_con->label, path, interface, member))
+ {
+ _dbus_string_free (&qstr);
+ goto oom;
+ }
+
+ res = aa_query_label (src_perm,
+ _dbus_string_get_data (&qstr),
+ _dbus_string_get_length (&qstr),
+ &src_allow, &src_audit);
+ _dbus_string_free (&qstr);
+ if (res == -1)
+ {
+ src_errno = errno;
+ set_error_from_query_errno (error, src_errno);
+ goto audit;
+ }
+ }
+
+ if (is_unconfined (dst_con->label, dst_con->mode))
+ {
+ dst_allow = TRUE;
+ dst_audit = FALSE;
+ }
+ else
+ {
+ if (!_dbus_string_init (&qstr))
+ goto oom;
+
+ if (!build_message_query (&qstr, dst_con->label, bustype, source,
+ src_con->label, path, interface, member))
+ {
+ _dbus_string_free (&qstr);
+ goto oom;
+ }
+
+ res = aa_query_label (dst_perm,
+ _dbus_string_get_data (&qstr),
+ _dbus_string_get_length (&qstr),
+ &dst_allow, &dst_audit);
+ _dbus_string_free (&qstr);
+ if (res == -1)
+ {
+ dst_errno = errno;
+ set_error_from_query_errno (error, dst_errno);
+ goto audit;
+ }
+ }
+
+ /* Don't fail operations on profiles in complain mode */
+ if (modestr_is_complain (src_con->mode))
+ src_allow = TRUE;
+ if (modestr_is_complain (dst_con->mode))
+ dst_allow = TRUE;
+
+ if (!src_allow || !dst_allow)
+ set_error_from_denied_message (error,
+ sender,
+ proposed_recipient,
+ requested_reply,
+ msgtypestr,
+ path,
+ interface,
+ member,
+ error_name,
+ destination);
+
+ /* Don't audit the message if one of the following conditions is true:
+ * 1) The AppArmor query indicates that auditing should not happen.
+ * 2) The message is a reply type. Reply message are not audited because
+ * the AppArmor policy language does not have the notion of a reply
+ * message. Unrequested replies will be silently discarded if the sender
+ * does not have permission to send to the receiver or if the receiver
+ * does not have permission to receive from the sender.
+ */
+ if ((!src_audit && !dst_audit) ||
+ (msgtype == DBUS_MESSAGE_TYPE_METHOD_RETURN ||
+ msgtype == DBUS_MESSAGE_TYPE_ERROR))
+ goto out;
+
+ audit:
+ if (!_dbus_string_init (&auxdata))
+ goto oom;
+ free_auxdata = TRUE;
+
+ if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown"))
+ goto oom;
+
+ if (path && !_dbus_append_pair_str (&auxdata, "path", path))
+ goto oom;
+
+ if (interface && !_dbus_append_pair_str (&auxdata, "interface", interface))
+ goto oom;
+
+ if (member && !_dbus_append_pair_str (&auxdata, "member", member))
+ goto oom;
+
+ if (error_name && !_dbus_append_pair_str (&auxdata, "error_name", error_name))
+ goto oom;
+
+ len = _dbus_string_get_length (&auxdata);
+
+ if (src_audit)
+ {
+ if (!_dbus_append_mask (&auxdata, src_perm))
+ goto oom;
+
+ if (destination && !_dbus_append_pair_str (&auxdata, "name", destination))
+ goto oom;
+
+ if (sender && dbus_connection_get_unix_process_id (sender, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "pid", pid))
+ goto oom;
+
+ if (src_con->label &&
+ !_dbus_append_pair_str (&auxdata, "label", src_con->label))
+ goto oom;
+
+ if (proposed_recipient &&
+ dbus_connection_get_unix_process_id (proposed_recipient, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "peer_pid", pid))
+ goto oom;
+
+ if (dst_con->label &&
+ !_dbus_append_pair_str (&auxdata, "peer_label", dst_con->label))
+ goto oom;
+
+ if (src_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (src_errno)))
+ goto oom;
+
+ if (dst_errno &&
+ !_dbus_append_pair_str (&auxdata, "peer_info", strerror (dst_errno)))
+ goto oom;
+
+ log_message (src_allow, msgtypestr, &auxdata);
+ }
+ if (dst_audit)
+ {
+ _dbus_string_set_length (&auxdata, len);
+
+ if (source && !_dbus_append_pair_str (&auxdata, "name", source))
+ goto oom;
+
+ if (!_dbus_append_mask (&auxdata, dst_perm))
+ goto oom;
+
+ if (proposed_recipient &&
+ dbus_connection_get_unix_process_id (proposed_recipient, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "pid", pid))
+ goto oom;
+
+ if (dst_con->label &&
+ !_dbus_append_pair_str (&auxdata, "label", dst_con->label))
+ goto oom;
+
+ if (sender && dbus_connection_get_unix_process_id (sender, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "peer_pid", pid))
+ goto oom;
+
+ if (src_con->label &&
+ !_dbus_append_pair_str (&auxdata, "peer_label", src_con->label))
+ goto oom;
+
+ if (dst_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (dst_errno)))
+ goto oom;
+
+ if (src_errno &&
+ !_dbus_append_pair_str (&auxdata, "peer_info", strerror (src_errno)))
+ goto oom;
+
+ log_message (dst_allow, msgtypestr, &auxdata);
+ }
+
+ out:
+ if (src_con != NULL)
+ bus_apparmor_confinement_unref (src_con);
+ if (dst_con != NULL)
+ bus_apparmor_confinement_unref (dst_con);
+ if (free_auxdata)
+ _dbus_string_free (&auxdata);
+
+ return src_allow && dst_allow;
+
+ oom:
+ if (error != NULL && !dbus_error_is_set (error))
+ BUS_SET_OOM (error);
+ src_allow = FALSE;
+ dst_allow = FALSE;
+ goto out;
+
+#else
+ return TRUE;
+#endif /* HAVE_APPARMOR */
+}
+
+/**
+ * Check if Apparmor security controls allow the connection to eavesdrop on
+ * other connections.
+ *
+ * @param connection the connection attempting to eavesdrop.
+ * @param bustype name of the bus
+ * @param error the reason for failure when FALSE is returned
+ * @returns TRUE if eavesdropping is permitted
+ */
+dbus_bool_t
+bus_apparmor_allows_eavesdropping (DBusConnection *connection,
+ const char *bustype,
+ DBusError *error)
+{
+#ifdef HAVE_APPARMOR
+ BusAppArmorConfinement *con = NULL;
+ DBusString qstr, auxdata;
+ dbus_bool_t allow = FALSE, audit = TRUE;
+ dbus_bool_t free_auxdata = FALSE;
+ unsigned long pid;
+ int res, serrno = 0;
+
+ if (!apparmor_enabled)
+ return TRUE;
+
+ con = bus_connection_dup_apparmor_confinement (connection);
+
+ if (is_unconfined (con->label, con->mode))
+ {
+ allow = TRUE;
+ audit = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_string_init (&qstr))
+ goto oom;
+
+ if (!build_eavesdrop_query (&qstr, con->label, bustype))
+ {
+ _dbus_string_free (&qstr);
+ goto oom;
+ }
+
+ res = aa_query_label (AA_DBUS_EAVESDROP,
+ _dbus_string_get_data (&qstr),
+ _dbus_string_get_length (&qstr),
+ &allow, &audit);
+ _dbus_string_free (&qstr);
+ if (res == -1)
+ {
+ serrno = errno;
+ set_error_from_query_errno (error, serrno);
+ goto audit;
+ }
+
+ /* Don't fail operations on profiles in complain mode */
+ if (modestr_is_complain (con->mode))
+ allow = TRUE;
+
+ if (!allow)
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Connection \"%s\" is not allowed to eavesdrop due to "
+ "AppArmor policy",
+ bus_connection_is_active (connection) ?
+ bus_connection_get_name (connection) : "(inactive)");
+
+ if (!audit)
+ goto out;
+
+ audit:
+ if (!_dbus_string_init (&auxdata))
+ goto oom;
+ free_auxdata = TRUE;
+
+ if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown"))
+ goto oom;
+
+ if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno)))
+ goto oom;
+
+ if (!_dbus_append_pair_str (&auxdata, "mask", "eavesdrop"))
+ goto oom;
+
+ if (connection && dbus_connection_get_unix_process_id (connection, &pid) &&
+ !_dbus_append_pair_uint (&auxdata, "pid", pid))
+ goto oom;
+
+ if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label))
+ goto oom;
+
+ log_message (allow, "eavesdrop", &auxdata);
+
+ out:
+ if (con != NULL)
+ bus_apparmor_confinement_unref (con);
+ if (free_auxdata)
+ _dbus_string_free (&auxdata);
+
+ return allow;
+
+ oom:
+ if (error != NULL && !dbus_error_is_set (error))
+ BUS_SET_OOM (error);
+ allow = FALSE;
+ goto out;
+
+#else
+ return TRUE;
+#endif /* HAVE_APPARMOR */
+}
diff --git a/bus/apparmor.h b/bus/apparmor.h
new file mode 100644
index 00000000..4a47aecc
--- /dev/null
+++ b/bus/apparmor.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ * apparmor.c AppArmor security checks for D-Bus
+ *
+ * Authors: John Johansen <john.johansen@canonical.com>
+ * Tyler Hicks <tyhicks@canonical.com>
+ * Based on: selinux.h by Matthew Rickard
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BUS_APPARMOR_H
+#define BUS_APPARMOR_H
+
+#include <dbus/dbus.h>
+#include "bus.h"
+
+void bus_apparmor_audit_init (void);
+dbus_bool_t bus_apparmor_pre_init (void);
+dbus_bool_t bus_apparmor_set_mode_from_config (const char *mode,
+ DBusError *error);
+dbus_bool_t bus_apparmor_full_init (DBusError *error);
+void bus_apparmor_shutdown (void);
+dbus_bool_t bus_apparmor_enabled (void);
+
+void bus_apparmor_confinement_unref (BusAppArmorConfinement *confinement);
+void bus_apparmor_confinement_ref (BusAppArmorConfinement *confinement);
+BusAppArmorConfinement* bus_apparmor_init_connection_confinement (DBusConnection *connection,
+ DBusError *error);
+
+dbus_bool_t bus_apparmor_allows_acquire_service (DBusConnection *connection,
+ const char *bustype,
+ const char *service_name,
+ DBusError *error);
+dbus_bool_t bus_apparmor_allows_send (DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ dbus_bool_t requested_reply,
+ const char *bustype,
+ int msgtype,
+ const char *path,
+ const char *interface,
+ const char *member,
+ const char *error_name,
+ const char *destination,
+ const char *source,
+ DBusError *error);
+
+dbus_bool_t bus_apparmor_allows_eavesdropping (DBusConnection *connection,
+ const char *bustype,
+ DBusError *error);
+
+#endif /* BUS_APPARMOR_H */
diff --git a/bus/bus.c b/bus/bus.c
index 091fbe28..68de1b22 100644
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -34,6 +34,7 @@
#include "config-parser.h"
#include "signals.h"
#include "selinux.h"
+#include "apparmor.h"
#include "dir-watch.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
@@ -933,6 +934,20 @@ bus_context_new (const DBusString *config_file,
bus_context_log (context, DBUS_SYSTEM_LOG_FATAL, "SELinux enabled but D-Bus initialization failed; check system log\n");
}
+ if (!bus_apparmor_full_init (error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (bus_apparmor_enabled ())
+ {
+ /* Only print AppArmor mediation message when syslog support is enabled */
+ if (context->syslog)
+ bus_context_log (context, DBUS_SYSTEM_LOG_INFO,
+ "AppArmor D-Bus mediation is enabled\n");
+ }
+
if (!process_config_postinit (context, parser, error))
{
_DBUS_ASSERT_ERROR_IS_SET (error);
@@ -960,6 +975,9 @@ bus_context_new (const DBusString *config_file,
/* FIXME - why not just put this in full_init() below? */
bus_selinux_audit_init ();
#endif
+#ifdef HAVE_APPARMOR
+ bus_apparmor_audit_init ();
+#endif
}
dbus_server_free_data_slot (&server_data_slot);
@@ -1503,7 +1521,7 @@ bus_context_check_security_policy (BusContext *context,
DBusMessage *message,
DBusError *error)
{
- const char *dest;
+ const char *src, *dest;
BusClientPolicy *sender_policy;
BusClientPolicy *recipient_policy;
dbus_int32_t toggles;
@@ -1512,6 +1530,7 @@ bus_context_check_security_policy (BusContext *context,
dbus_bool_t requested_reply;
type = dbus_message_get_type (message);
+ src = dbus_message_get_sender (message);
dest = dbus_message_get_destination (message);
/* dispatch.c was supposed to ensure these invariants */
@@ -1544,30 +1563,6 @@ 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,
- dbus_message_type_to_string (dbus_message_get_type (message)),
- dbus_message_get_interface (message),
- dbus_message_get_member (message),
- dbus_message_get_error_name (message),
- dest ? dest : DBUS_SERVICE_DBUS, error))
- {
- if (error != NULL && !dbus_error_is_set (error))
- {
- /* don't syslog this, just set the error: avc_has_perm should
- * have already written to either the audit log or syslog */
- complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
- "An SELinux policy prevents this sender from sending this "
- "message to this recipient",
- 0, message, sender, proposed_recipient, FALSE, FALSE, error);
- _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);
@@ -1598,6 +1593,51 @@ bus_context_check_security_policy (BusContext *context,
}
else
{
+ sender_policy = NULL;
+ }
+
+ /* First verify the SELinux access controls. If allowed then
+ * go on with the standard checks.
+ */
+ if (!bus_selinux_allows_send (sender, proposed_recipient,
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_error_name (message),
+ dest ? dest : DBUS_SERVICE_DBUS, error))
+ {
+ if (error != NULL && !dbus_error_is_set (error))
+ {
+ /* don't syslog this, just set the error: avc_has_perm should
+ * have already written to either the audit log or syslog */
+ complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
+ "An SELinux policy prevents this sender from sending this "
+ "message to this recipient",
+ 0, message, sender, proposed_recipient, FALSE, FALSE, error);
+ _dbus_verbose ("SELinux security check denying send to service\n");
+ }
+
+ return FALSE;
+ }
+
+ /* next verify AppArmor access controls. If allowed then
+ * go on with the standard checks.
+ */
+ if (!bus_apparmor_allows_send (sender, proposed_recipient,
+ requested_reply,
+ bus_context_get_type (context),
+ dbus_message_get_type (message),
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_error_name (message),
+ dest ? dest : DBUS_SERVICE_DBUS,
+ src ? src : DBUS_SERVICE_DBUS,
+ error))
+ return FALSE;
+
+ if (!bus_connection_is_active (sender))
+ {
/* Policy for inactive connections is that they can only send
* the hello message to the bus driver
*/
diff --git a/bus/bus.h b/bus/bus.h
index 57ad5c78..3fab59ff 100644
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -38,6 +38,7 @@ typedef struct BusClientPolicy BusClientPolicy;
typedef struct BusPolicyRule BusPolicyRule;
typedef struct BusRegistry BusRegistry;
typedef struct BusSELinuxID BusSELinuxID;
+typedef struct BusAppArmorConfinement BusAppArmorConfinement;
typedef struct BusService BusService;
typedef struct BusOwner BusOwner;
typedef struct BusTransaction BusTransaction;
diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c
index c522ff49..5db6b289 100644
--- a/bus/config-parser-common.c
+++ b/bus/config-parser-common.c
@@ -127,6 +127,10 @@ bus_config_parser_element_name_to_type (const char *name)
{
return ELEMENT_ALLOW_ANONYMOUS;
}
+ else if (strcmp (name, "apparmor") == 0)
+ {
+ return ELEMENT_APPARMOR;
+ }
return ELEMENT_NONE;
}
@@ -181,6 +185,8 @@ bus_config_parser_element_type_to_name (ElementType type)
return "keep_umask";
case ELEMENT_ALLOW_ANONYMOUS:
return "allow_anonymous";
+ case ELEMENT_APPARMOR:
+ return "apparmor";
}
_dbus_assert_not_reached ("bad element type");
diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h
index 186bf4cf..382a0141 100644
--- a/bus/config-parser-common.h
+++ b/bus/config-parser-common.h
@@ -49,7 +49,8 @@ typedef enum
ELEMENT_STANDARD_SYSTEM_SERVICEDIRS,
ELEMENT_KEEP_UMASK,
ELEMENT_SYSLOG,
- ELEMENT_ALLOW_ANONYMOUS
+ ELEMENT_ALLOW_ANONYMOUS,
+ ELEMENT_APPARMOR
} ElementType;
ElementType bus_config_parser_element_name_to_type (const char *element_name);
diff --git a/bus/config-parser.c b/bus/config-parser.c
index ee2d4e7d..58048a50 100644
--- a/bus/config-parser.c
+++ b/bus/config-parser.c
@@ -28,6 +28,7 @@
#include "utils.h"
#include "policy.h"
#include "selinux.h"
+#include "apparmor.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-internals.h>
#include <dbus/dbus-misc.h>
@@ -1136,6 +1137,27 @@ start_busconfig_child (BusConfigParser *parser,
return TRUE;
}
+ else if (element_type == ELEMENT_APPARMOR)
+ {
+ Element *e;
+ const char *mode;
+
+ if ((e = push_element (parser, ELEMENT_APPARMOR)) == NULL)
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ if (!locate_attributes (parser, "apparmor",
+ attribute_names,
+ attribute_values,
+ error,
+ "mode", &mode,
+ NULL))
+ return FALSE;
+
+ return bus_apparmor_set_mode_from_config (mode, error);
+ }
else
{
dbus_set_error (error, DBUS_ERROR_FAILED,
@@ -2074,6 +2096,7 @@ bus_config_parser_end_element (BusConfigParser *parser,
case ELEMENT_STANDARD_SESSION_SERVICEDIRS:
case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
case ELEMENT_ALLOW_ANONYMOUS:
+ case ELEMENT_APPARMOR:
break;
}
@@ -2373,6 +2396,7 @@ bus_config_parser_content (BusConfigParser *parser,
case ELEMENT_ALLOW_ANONYMOUS:
case ELEMENT_SELINUX:
case ELEMENT_ASSOCIATE:
+ case ELEMENT_APPARMOR:
if (all_whitespace (content))
return TRUE;
else
diff --git a/bus/connection.c b/bus/connection.c
index 64da1292..690f3a5d 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -30,6 +30,7 @@
#include "signals.h"
#include "expirelist.h"
#include "selinux.h"
+#include "apparmor.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-timeout.h>
@@ -99,6 +100,7 @@ typedef struct
char *cached_loginfo_string;
BusSELinuxID *selinux_id;
+ BusAppArmorConfinement *apparmor_confinement;
long connection_tv_sec; /**< Time when we connected (seconds component) */
long connection_tv_usec; /**< Time when we connected (microsec component) */
@@ -439,6 +441,9 @@ free_connection_data (void *data)
if (d->selinux_id)
bus_selinux_id_unref (d->selinux_id);
+
+ if (d->apparmor_confinement)
+ bus_apparmor_confinement_unref (d->apparmor_confinement);
dbus_free (d->cached_loginfo_string);
@@ -714,6 +719,19 @@ bus_connections_setup_connection (BusConnections *connections,
goto out;
}
+ d->apparmor_confinement = bus_apparmor_init_connection_confinement (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,
remove_connection_watch,
@@ -801,6 +819,10 @@ bus_connections_setup_connection (BusConnections *connections,
if (d->selinux_id)
bus_selinux_id_unref (d->selinux_id);
d->selinux_id = NULL;
+
+ if (d->apparmor_confinement)
+ bus_apparmor_confinement_unref (d->apparmor_confinement);
+ d->apparmor_confinement = NULL;
if (!dbus_connection_set_watch_functions (connection,
NULL, NULL, NULL,
@@ -1201,6 +1223,19 @@ bus_connection_get_selinux_id (DBusConnection *connection)
return d->selinux_id;
}
+BusAppArmorConfinement*
+bus_connection_dup_apparmor_confinement (DBusConnection *connection)
+{
+ BusConnectionData *d;
+
+ d = BUS_CONNECTION_DATA (connection);
+
+ _dbus_assert (d != NULL);
+
+ bus_apparmor_confinement_ref (d->apparmor_confinement);
+ return d->apparmor_confinement;
+}
+
/**
* Checks whether the connection is registered with the message bus.
*
diff --git a/bus/connection.h b/bus/connection.h
index dca22633..8c68d0a0 100644
--- a/bus/connection.h
+++ b/bus/connection.h
@@ -54,6 +54,7 @@ BusActivation* bus_connection_get_activation (DBusConnection
BusMatchmaker* bus_connection_get_matchmaker (DBusConnection *connection);
const char * bus_connection_get_loginfo (DBusConnection *connection);
BusSELinuxID* bus_connection_get_selinux_id (DBusConnection *connection);
+BusAppArmorConfinement* bus_connection_dup_apparmor_confinement (DBusConnection *connection);
dbus_bool_t bus_connections_check_limits (BusConnections *connections,
DBusConnection *requesting_completion,
DBusError *error);
diff --git a/bus/driver.c b/bus/driver.c
index ceebb6f2..aab922ae 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -24,6 +24,7 @@
#include <config.h>
#include "activation.h"
+#include "apparmor.h"
#include "connection.h"
#include "driver.h"
#include "dispatch.h"
@@ -34,10 +35,12 @@
#include "utils.h"
#include <dbus/dbus-asv-util.h>
+#include <dbus/dbus-connection-internal.h>
#include <dbus/dbus-string.h>
#include <dbus/dbus-internals.h>
#include <dbus/dbus-message.h>
#include <dbus/dbus-marshal-recursive.h>
+#include <dbus/dbus-marshal-validate.h>
#include <string.h>
static DBusConnection *
@@ -1108,9 +1111,10 @@ bus_driver_handle_add_match (DBusConnection *connection,
DBusError *error)
{
BusMatchRule *rule;
- const char *text;
+ const char *text, *bustype;
DBusString str;
BusMatchmaker *matchmaker;
+ BusContext *context;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -1143,6 +1147,12 @@ bus_driver_handle_add_match (DBusConnection *connection,
if (rule == NULL)
goto failed;
+ context = bus_transaction_get_context (transaction);
+ bustype = context ? bus_context_get_type (context) : NULL;
+ if (bus_match_rule_get_client_is_eavesdropping (rule) &&
+ !bus_apparmor_allows_eavesdropping (connection, bustype, error))
+ goto failed;
+
matchmaker = bus_connection_get_matchmaker (connection);
if (!bus_matchmaker_add_rule (matchmaker, rule))
@@ -1646,6 +1656,7 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
DBusMessageIter reply_iter;
DBusMessageIter array_iter;
unsigned long ulong_val;
+ char *s;
const char *service;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
@@ -1680,6 +1691,45 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection,
goto oom;
}
+ if (dbus_connection_get_windows_user (conn, &s))
+ {
+ DBusString str;
+ dbus_bool_t result;
+
+ if (s == NULL)
+ goto oom;
+
+ _dbus_string_init_const (&str, s);
+ result = _dbus_validate_utf8 (&str, 0, _dbus_string_get_length (&str));
+ _dbus_string_free (&str);
+ if (result)
+ {
+ if (!_dbus_asv_add_string (&array_iter, "WindowsSID", s))
+ {
+ dbus_free (s);
+ goto oom;
+ }
+ }
+ dbus_free (s);
+ }
+
+ if (_dbus_connection_get_linux_security_label (conn, &s))
+ {
+ if (s == NULL)
+ goto oom;
+
+ /* use the GVariant bytestring convention for strings of unknown
+ * encoding: include the \0 in the payload, for zero-copy reading */
+ if (!_dbus_asv_add_byte_array (&array_iter, "LinuxSecurityLabel",
+ s, strlen (s) + 1))
+ {
+ dbus_free (s);
+ goto oom;
+ }
+
+ dbus_free (s);
+ }
+
if (!_dbus_asv_close (&reply_iter, &array_iter))
goto oom;
@@ -1746,6 +1796,72 @@ bus_driver_handle_reload_config (DBusConnection *connection,
return FALSE;
}
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+static dbus_bool_t
+bus_driver_handle_enable_verbose (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ _dbus_set_verbose(TRUE);
+
+ dbus_message_unref (reply);
+ return TRUE;
+
+ oom:
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ BUS_SET_OOM (error);
+
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+
+static dbus_bool_t
+bus_driver_handle_disable_verbose (DBusConnection *connection,
+ BusTransaction *transaction,
+ DBusMessage *message,
+ DBusError *error)
+{
+ DBusMessage *reply = NULL;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ goto oom;
+
+ if (! bus_transaction_send_from_driver (transaction, connection, reply))
+ goto oom;
+
+ _dbus_set_verbose(FALSE);
+
+ dbus_message_unref (reply);
+ return TRUE;
+
+ oom:
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ BUS_SET_OOM (error);
+
+ if (reply)
+ dbus_message_unref (reply);
+ return FALSE;
+}
+#endif
+
static dbus_bool_t
bus_driver_handle_get_id (DBusConnection *connection,
BusTransaction *transaction,
@@ -1808,6 +1924,8 @@ bus_driver_handle_become_monitor (DBusConnection *connection,
DBusError *error)
{
char **match_rules = NULL;
+ const char *bustype;
+ BusContext *context;
BusMatchRule *rule;
DBusList *rules = NULL;
DBusList *iter;
@@ -1822,6 +1940,11 @@ bus_driver_handle_become_monitor (DBusConnection *connection,
if (!bus_driver_check_message_is_for_us (message, error))
goto out;
+ context = bus_transaction_get_context (transaction);
+ bustype = context ? bus_context_get_type (context) : NULL;
+ if (!bus_apparmor_allows_eavesdropping (connection, bustype, error))
+ goto out;
+
if (!bus_driver_check_caller_is_privileged (connection, transaction,
message, error))
goto out;
@@ -2018,6 +2141,14 @@ static const MessageHandler monitoring_message_handlers[] = {
{ NULL, NULL, NULL, NULL }
};
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+static const MessageHandler verbose_message_handlers[] = {
+ { "EnableVerbose", "", "", bus_driver_handle_enable_verbose},
+ { "DisableVerbose", "", "", bus_driver_handle_disable_verbose},
+ { NULL, NULL, NULL, NULL }
+};
+#endif
+
#ifdef DBUS_ENABLE_STATS
static const MessageHandler stats_message_handlers[] = {
{ "GetStats", "", "a{sv}", bus_stats_handle_get_stats },
@@ -2050,6 +2181,9 @@ static InterfaceHandler interface_handlers[] = {
" </signal>\n" },
{ DBUS_INTERFACE_INTROSPECTABLE, introspectable_message_handlers, NULL },
{ DBUS_INTERFACE_MONITORING, monitoring_message_handlers, NULL },
+#ifdef DBUS_ENABLE_VERBOSE_MODE
+ { DBUS_INTERFACE_VERBOSE, verbose_message_handlers, NULL },
+#endif
#ifdef DBUS_ENABLE_STATS
{ BUS_INTERFACE_STATS, stats_message_handlers, NULL },
#endif
diff --git a/bus/main.c b/bus/main.c
index e060baa8..b48f03f3 100644
--- a/bus/main.c
+++ b/bus/main.c
@@ -39,6 +39,7 @@
#include <unistd.h> /* for write() and STDERR_FILENO */
#endif
#include "selinux.h"
+#include "apparmor.h"
static BusContext *context;
@@ -614,6 +615,12 @@ main (int argc, char **argv)
exit (1);
}
+ if (!bus_apparmor_pre_init ())
+ {
+ _dbus_warn ("AppArmor pre-initialization failed: out of memory\n");
+ exit (1);
+ }
+
dbus_error_init (&error);
context = bus_context_new (&config_file, flags,
&print_addr_pipe, &print_pid_pipe,
@@ -649,6 +656,7 @@ main (int argc, char **argv)
bus_context_shutdown (context);
bus_context_unref (context);
bus_selinux_shutdown ();
+ bus_apparmor_shutdown ();
return 0;
}
diff --git a/bus/services.c b/bus/services.c
index 584485b1..8e625867 100644
--- a/bus/services.c
+++ b/bus/services.c
@@ -36,6 +36,7 @@
#include "policy.h"
#include "bus.h"
#include "selinux.h"
+#include "apparmor.h"
struct BusService
{
@@ -458,6 +459,12 @@ bus_registry_acquire_service (BusRegistry *registry,
_dbus_string_get_const_data (service_name));
goto out;
}
+
+ if (!bus_apparmor_allows_acquire_service (connection,
+ (registry->context ?
+ bus_context_get_type (registry->context) : NULL),
+ _dbus_string_get_const_data (service_name), error))
+ goto out;
if (!bus_client_policy_check_can_own (policy, service_name))
{
diff --git a/bus/session.conf.in b/bus/session.conf.in
index cfe9544f..d2e3f2d0 100644
--- a/bus/session.conf.in
+++ b/bus/session.conf.in
@@ -25,6 +25,9 @@
<allow own="*"/>
</policy>
+ <!-- Enable AppArmor mediation when it is available -->
+ <apparmor mode="enabled"/>
+
<!-- Config files are placed here that among other things,
further restrict the above policy for specific services. -->
<includedir>session.d</includedir>
diff --git a/bus/signals.c b/bus/signals.c
index 4390028f..260dd243 100644
--- a/bus/signals.c
+++ b/bus/signals.c
@@ -411,6 +411,15 @@ bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
}
dbus_bool_t
+bus_match_rule_get_client_is_eavesdropping (BusMatchRule *rule)
+{
+ if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+dbus_bool_t
bus_match_rule_set_path (BusMatchRule *rule,
const char *path,
dbus_bool_t is_namespace)
diff --git a/bus/signals.h b/bus/signals.h
index d19fc7c5..0edfb07e 100644
--- a/bus/signals.h
+++ b/bus/signals.h
@@ -73,6 +73,8 @@ dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule,
void bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule,
dbus_bool_t is_eavesdropping);
+dbus_bool_t bus_match_rule_get_client_is_eavesdropping (BusMatchRule *rule);
+
BusMatchRule* bus_match_rule_parse (DBusConnection *matches_go_to,
const DBusString *rule_text,
DBusError *error);
diff --git a/bus/system.conf.in b/bus/system.conf.in
index ac78c734..fc472bd7 100644
--- a/bus/system.conf.in
+++ b/bus/system.conf.in
@@ -97,6 +97,9 @@
send_interface="org.freedesktop.DBus.Debug.Stats"/>
</policy>
+ <!-- Enable AppArmor mediation when it is available -->
+ <apparmor mode="enabled"/>
+
<!-- Config files are placed here that among other things, punch
holes in the above policy for specific services. -->
<includedir>system.d</includedir>