From 88c7b6f3c6af22f19089fc9597258963559bd8f0 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 13 Feb 2014 13:17:23 -0600 Subject: [PATCH v3 11/13] Mediation of processes eavesdropping When an AppArmor confined process wants to eavesdrop on a bus, a check is performed to see if the action should be allowed. The check is based on the connection's label and the bus type. This patch adds a new hook, which was not previously included in the SELinux mediation, to mediate eavesdropping from bus_driver_handle_add_match(). A new function is added to bus/signals.c to see if a match rule is an eavesdropping rule since the rule flags field is private to signals.c. An example AppArmor rule that would allow a process to eavesdrop on the session bus would be: dbus eavesdrop bus=session, Bug: https://bugs.freedesktop.org/show_bug.cgi?id=75113 Signed-off-by: Tyler Hicks --- * Changes in v2: - Fix up minor conflicts from changes in earlier patches - Fix coding style issue of bus_apparmor_allows_eavesdropping() declaration - Call the newly named modestr_is_complain() function - Adjust build_query() call sites to use DBusString for the query - Create build_eavesdrop_query() to get rid of variable length build_query() args - Use the renamed bus_connection_dup_apparmor_confinement() function - Always set the DBusError from inside bus_apparmor_allows_eavesdropping() when returning FALSE - Rename string_alloced variable to free_auxdata - Fix bad (bustype && foo (bustype ? bustype: "unknown")) logic - Use temp variable in bus_driver_handle_add_match() when passing the bustype to bus_apparmor_allows_eavesdropping() * Changes in v3: - Added Bug link in commit message - Document all bus_apparmor_allows_eavesdropping() parameters bus/apparmor.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bus/apparmor.h | 4 ++ bus/driver.c | 10 ++++- bus/signals.c | 9 +++++ bus/signals.h | 2 + 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/bus/apparmor.c b/bus/apparmor.c index 05c9704..bcae4db 100644 --- a/bus/apparmor.c +++ b/bus/apparmor.c @@ -298,6 +298,12 @@ build_message_query (DBusString *query, 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) { @@ -950,3 +956,114 @@ bus_apparmor_allows_send (DBusConnection *sender, 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->context, con->mode)) + { + allow = TRUE; + audit = FALSE; + goto out; + } + + if (!_dbus_string_init (&qstr)) + goto oom; + + if (!build_eavesdrop_query (&qstr, con->context, 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->context && !_dbus_append_pair_str (&auxdata, "profile", con->context)) + 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 index bdc601d..625b67f 100644 --- a/bus/apparmor.h +++ b/bus/apparmor.h @@ -59,4 +59,8 @@ dbus_bool_t bus_apparmor_allows_send (DBusConnection *sender, 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/driver.c b/bus/driver.c index e95a79d..820c48a 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -24,6 +24,7 @@ #include #include "activation.h" +#include "apparmor.h" #include "connection.h" #include "driver.h" #include "dispatch.h" @@ -991,9 +992,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); @@ -1026,6 +1028,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)) diff --git a/bus/signals.c b/bus/signals.c index 4c500c6..0642de5 100644 --- a/bus/signals.c +++ b/bus/signals.c @@ -379,6 +379,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 a71d2e4..5db87fd 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); -- 1.9.1