From 2a881a9e75526986b3df1c999a78bd8326d77082 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 12 Feb 2014 12:37:41 -0600 Subject: [PATCH 05/14] Initialize AppArmor mediation When starting dbus-daemon, autodetect AppArmor kernel support and use the results from parsing the busconfig to determine if mediation should be enabled. In the busconfig, "enabled" means that kernel support is autodetected and, if available, AppArmor mediation occurs in dbus-daemon. In "enabled" mode, if kernel support is not detected, mediation is disabled. "disabled" means that mediation does not occur. "required" means that kernel support must be detected for dbus-daemon to start. Additionally, when libaudit support is built into dbus-daemon, the AppArmor initialization routines set up the audit connection. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=75113 Signed-off-by: John Johansen [tyhicks: Honor enforcement modes and detect AppArmor dbus rule support] [tyhicks: fix unreachable return when AppArmor support is built] [tyhicks: make bus_apparmor_full_init() able to raise a DBusError] Signed-off-by: Tyler Hicks [smcv: _bus_apparmor_aa_supports_dbus: document necessary kernel API guarantee] [smcv: bus_apparmor_pre_init: distinguish between OOM and AppArmor not enabled] [smcv: document why we open() and not just stat()] Reviewed-by: Simon McVittie Reviewed-by: Tyler Hicks --- bus/apparmor.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bus/apparmor.h | 5 ++ bus/bus.c | 18 ++++++ bus/main.c | 8 +++ 4 files changed, 208 insertions(+) diff --git a/bus/apparmor.c b/bus/apparmor.c index b53d051..645bb84 100644 --- a/bus/apparmor.c +++ b/bus/apparmor.c @@ -29,7 +29,27 @@ #ifdef HAVE_APPARMOR #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include + +#ifdef HAVE_LIBAUDIT +#include +#include +#else +#include +#endif /* HAVE_LIBAUDIT */ + +/* Store the value telling us if AppArmor D-Bus mediation is enabled. */ +static dbus_bool_t apparmor_enabled = FALSE; typedef enum { APPARMOR_DISABLED, @@ -40,8 +60,104 @@ typedef enum { /* 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 + +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; +} #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) { @@ -77,3 +193,64 @@ bus_apparmor_set_mode_from_config (const char *mode, DBusError *error) 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 context. + */ +dbus_bool_t +bus_apparmor_full_init (DBusError *error) +{ +#ifdef HAVE_APPARMOR + if (apparmor_enabled) + { + if (apparmor_config_mode == APPARMOR_DISABLED) + { + apparmor_enabled = FALSE; + return TRUE; + } + } + else + { + if (apparmor_config_mode == APPARMOR_REQUIRED) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "AppArmor mediation required but not present\n"); + 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"); + +#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 +} diff --git a/bus/apparmor.h b/bus/apparmor.h index 0d6f274..66a77c0 100644 --- a/bus/apparmor.h +++ b/bus/apparmor.h @@ -28,7 +28,12 @@ #include +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); #endif /* BUS_APPARMOR_H */ diff --git a/bus/bus.c b/bus/bus.c index 091fbe2..2c2c564 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 #include @@ -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); diff --git a/bus/main.c b/bus/main.c index e060baa..b48f03f 100644 --- a/bus/main.c +++ b/bus/main.c @@ -39,6 +39,7 @@ #include /* 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; } -- 2.1.4