summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bus/apparmor.c285
-rw-r--r--bus/apparmor.h6
-rw-r--r--bus/connection.c13
-rw-r--r--bus/connection.h1
-rw-r--r--bus/services.c7
5 files changed, 310 insertions, 2 deletions
diff --git a/bus/apparmor.c b/bus/apparmor.c
index d22ac672..3a2426a5 100644
--- a/bus/apparmor.c
+++ b/bus/apparmor.c
@@ -39,15 +39,15 @@
#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>
-#else
-#include <syslog.h>
#endif /* HAVE_LIBAUDIT */
+#include "connection.h"
#include "utils.h"
/* Store the value telling us if AppArmor D-Bus mediation is enabled. */
@@ -198,6 +198,154 @@ out:
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 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));
+}
#endif /* HAVE_APPARMOR */
/**
@@ -342,6 +490,20 @@ bus_apparmor_enabled (void)
#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)
@@ -387,3 +549,122 @@ bus_apparmor_init_connection_confinement (DBusConnection *connection,
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->context, con->mode))
+ {
+ allow = TRUE;
+ audit = FALSE;
+ goto out;
+ }
+
+ if (!_dbus_string_init (&qstr))
+ goto oom;
+
+ if (!build_service_query (&qstr, con->context, 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->context && !_dbus_append_pair_str (&auxdata, "profile", con->context))
+ 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 */
+}
diff --git a/bus/apparmor.h b/bus/apparmor.h
index 861094e7..4f57c8b3 100644
--- a/bus/apparmor.h
+++ b/bus/apparmor.h
@@ -38,7 +38,13 @@ 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);
+
#endif /* BUS_APPARMOR_H */
diff --git a/bus/connection.c b/bus/connection.c
index 93f99674..690f3a5d 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -1223,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/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))
{