diff --git a/mission-control-plugins/dbus-acl.c b/mission-control-plugins/dbus-acl.c index 652f18b..4fcd111 100644 --- a/mission-control-plugins/dbus-acl.c +++ b/mission-control-plugins/dbus-acl.c @@ -58,6 +58,8 @@ * A single object can implement more than one interface. */ +#include "config.h" + #include #include #include @@ -66,8 +68,8 @@ #ifdef ENABLE_DEBUG #define DEBUG(_p, _format, ...) \ - g_debug ("%s: %s: " _format, G_STRFUNC, \ - (_p != NULL) ? mcp_dbus_acl_name (_p) : "NULL", ##__VA_ARGS__) + g_debug ("dbus-acl: %s: %s: " _format, G_STRFUNC, \ + (_p != NULL) ? mcp_dbus_acl_name (_p) : "-", ##__VA_ARGS__) #else /* ENABLE_DEBUG */ @@ -288,7 +290,7 @@ mcp_dbus_acl_authorised_async_step (DBusAclAuthData *ad, McpDBusAclIface *iface = MCP_DBUS_ACL_GET_IFACE (plugin); if (ad->acl != NULL) - DEBUG (ad->acl, ":A: passed ACL for %s", ad->name); + DEBUG (ad->acl, "passed ACL for %s", ad->name); /* take the next plugin off the next_acl list */ ad->next_acl = g_list_next (ad->next_acl); @@ -303,7 +305,7 @@ mcp_dbus_acl_authorised_async_step (DBusAclAuthData *ad, else /* reached the end of the plugin list: call actual handler */ { if (ad->acl != NULL) - DEBUG (ad->acl, ":B: passed ACL for %s", ad->name); + DEBUG (ad->acl, "passed final ACL for %s", ad->name); ad->handler (ad->context, ad->data); } @@ -374,6 +376,9 @@ mcp_dbus_acl_authorised_async (TpDBusDaemon *dbus, ad->handler = handler; ad->next_acl = acls; + DEBUG (NULL, "DBus access ACL verification: %u rules for %s", + g_list_length (acls), + name); mcp_dbus_acl_authorised_async_step (ad, TRUE); } diff --git a/mission-control-plugins/dbus-acl.h b/mission-control-plugins/dbus-acl.h index 3ed3abc..8567b18 100644 --- a/mission-control-plugins/dbus-acl.h +++ b/mission-control-plugins/dbus-acl.h @@ -112,9 +112,9 @@ void mcp_dbus_acl_iface_implement_authorised (McpDBusAclIface *iface, void mcp_dbus_acl_iface_implement_authorised_async (McpDBusAclIface *iface, DBusAclAsyncAuthoriser method); -const gchar *mcp_dbus_acl_name (const McpDBusAcl *storage); +const gchar *mcp_dbus_acl_name (const McpDBusAcl *acl); -const gchar *mcp_dbus_acl_description (const McpDBusAcl *storage); +const gchar *mcp_dbus_acl_description (const McpDBusAcl *acl); G_END_DECLS diff --git a/mission-control-plugins/dispatch-operation-policy.c b/mission-control-plugins/dispatch-operation-policy.c index b0e2f36..2aa7ddc 100644 --- a/mission-control-plugins/dispatch-operation-policy.c +++ b/mission-control-plugins/dispatch-operation-policy.c @@ -36,10 +36,7 @@ * #McpDispatchOperationPolicy, then return an instance of that subclass from * mcp_plugin_ref_nth_object(). * - * The contents of the #McpDispatchOperationPolicyIface struct are not public, - * so to provide an implementation of the check method, - * plugins should call mcp_dispatch_operation_policy_iface_implement_check() - * from the interface initialization function, like this: + * A typical plugin might look like this: * * * G_DEFINE_TYPE_WITH_CODE (MyPlugin, my_plugin, @@ -53,8 +50,8 @@ * cdo_policy_iface_init (McpDispatchOperationPolicyIface *iface, * gpointer unused G_GNUC_UNUSED) * { - * mcp_dispatch_operation_policy_iface_implement_check (iface, - * my_plugin_check_cdo); + * iface->check = my_plugin_check_cdo; + * iface->handler_is_suitable = my_plugin_handler_is_suitable; * } * * @@ -64,12 +61,6 @@ #include -struct _McpDispatchOperationPolicyIface { - GTypeInterface parent; - - void (*check) (McpDispatchOperationPolicy *, McpDispatchOperation *); -}; - GType mcp_dispatch_operation_policy_get_type (void) { @@ -102,6 +93,39 @@ mcp_dispatch_operation_policy_get_type (void) } /** + * McpDispatchOperationPolicyIface: + * @parent: the parent type + * @check: an implementation of mcp_dispatch_operation_policy_check(); + * %NULL is equivalent to an implementation that does nothing + * @handler_is_suitable: an implementation of + * mcp_dispatch_operation_policy_handler_is_suitable(); + * %NULL is equivalent to an implementation that accepts everything, + * i.e. always returns %TRUE + */ + +/** + * McpDispatchOperationPolicyCb: + * @policy: an implementation of this interface, provided by a plugin + * @dispatch_operation: an object representing a dispatch operation, i.e. + * a bundle of channels being dispatched + * + * Signature of an implementation of mcp_dispatch_operation_policy_check(). + */ + +/** + * McpDispatchOperationPolicyClientPredicate: + * @policy: an implementation of this interface, provided by a plugin + * @client: a Telepathy Client + * @dispatch_operation: an object representing a dispatch operation, i.e. + * a bundle of channels being dispatched + * + * Signature of a virtual method to ask a question about a Client in the + * context of a dispatch operation, synchronously. + * + * Returns: a boolean result + */ + +/** * mcp_dispatch_operation_policy_check: * @policy: an implementation of this interface, provided by a plugin * @dispatch_operation: an object representing a dispatch operation, i.e. @@ -134,11 +158,48 @@ mcp_dispatch_operation_policy_check (McpDispatchOperationPolicy *policy, * @iface: the interface * @impl: an implementation of the virtual method * mcp_dispatch_operation_policy_check() + * + * This method is no longer necessary: just set iface->check = impl instead. */ void mcp_dispatch_operation_policy_iface_implement_check ( McpDispatchOperationPolicyIface *iface, - void (*impl) (McpDispatchOperationPolicy *, McpDispatchOperation *)) + McpDispatchOperationPolicyCb impl) { iface->check = impl; } + +/** + * mcp_dispatch_operation_policy_handler_is_suitable: + * @policy: an implementation of this interface, provided by a plugin + * @handler: a proxy for the Handler's D-Bus API + * @dispatch_operation: an object representing a dispatch operation, i.e. + * a bundle of channels being dispatched + * + * Check whether a handler is "suitable" for these channels. For instance, + * this could be used to ensure that only the platform's default UI can be + * used for particular channels, even if MC would normally consider + * a third-party UI to be a better match. + * + * Mission Control calls all implementations of this method in turn, stopping + * when one of them returns %FALSE or when all implementations have been + * called. If they all return %TRUE, the handler is considered to be suitable. + * + * Returns: %TRUE if @handler handle @dispatch_operation + */ +gboolean +mcp_dispatch_operation_policy_handler_is_suitable ( + McpDispatchOperationPolicy *policy, + TpProxy *handler, + McpDispatchOperation *dispatch_operation) +{ + McpDispatchOperationPolicyIface *iface = + MCP_DISPATCH_OPERATION_POLICY_GET_IFACE (policy); + + g_return_val_if_fail (iface != NULL, TRUE); + + if (iface->handler_is_suitable != NULL) + return iface->handler_is_suitable (policy, handler, dispatch_operation); + else + return TRUE; +} diff --git a/mission-control-plugins/dispatch-operation-policy.h b/mission-control-plugins/dispatch-operation-policy.h index 8f0f7f4..810e353 100644 --- a/mission-control-plugins/dispatch-operation-policy.h +++ b/mission-control-plugins/dispatch-operation-policy.h @@ -48,13 +48,34 @@ typedef struct _McpDispatchOperationPolicyIface McpDispatchOperationPolicyIface; GType mcp_dispatch_operation_policy_get_type (void) G_GNUC_CONST; /* virtual methods */ + +typedef void (*McpDispatchOperationPolicyCb) ( + McpDispatchOperationPolicy *policy, + McpDispatchOperation *dispatch_operation); + void mcp_dispatch_operation_policy_check (McpDispatchOperationPolicy *policy, McpDispatchOperation *dispatch_operation); -/* vtable manipulation - the vtable is private to allow for expansion */ +typedef gboolean (*McpDispatchOperationPolicyClientPredicate) ( + McpDispatchOperationPolicy *policy, + TpProxy *client, + McpDispatchOperation *dispatch_operation); + +gboolean mcp_dispatch_operation_policy_handler_is_suitable ( + McpDispatchOperationPolicy *policy, + TpProxy *handler, + McpDispatchOperation *dispatch_operation); + void mcp_dispatch_operation_policy_iface_implement_check ( McpDispatchOperationPolicyIface *iface, - void (*impl) (McpDispatchOperationPolicy *, McpDispatchOperation *)); + McpDispatchOperationPolicyCb impl); + +struct _McpDispatchOperationPolicyIface { + GTypeInterface parent; + + McpDispatchOperationPolicyCb check; + McpDispatchOperationPolicyClientPredicate handler_is_suitable; +}; G_END_DECLS diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d801724..23ccc6c 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -7,21 +7,14 @@ INCLUDES = \ -DLIBDIR="@libdir@" \ -DLIBVERSION="@MCP_ABI_VERSION@" -EXTRA_DIST = - -instdir = @libdir@/mission-control-plugins.@MCP_ABI_VERSION@ - -plugin_LDFLAGS = -module -shared -avoid-version -rpath @abs_builddir@ - -inst_LTLIBRARIES = +noinst_LTLIBRARIES = if ENABLE_AEGIS -inst_LTLIBRARIES += mcp-dbus-aegis-acl.la - -mcp_dbus_aegis_acl_la_SOURCES = mcp-dbus-aegis-acl.c -mcp_dbus_aegis_acl_la_LDFLAGS = \ - $(plugin_LDFLAGS) \ +noinst_LTLIBRARIES += libmcp-aegis.la +libmcp_aegis_la_SOURCES = mcp-dbus-aegis-acl.c +libmcp_aegis_la_LIBADD = \ + $(top_builddir)/mission-control-plugins/libmission-control-plugins.la \ $(TELEPATHY_LIBS) \ $(DBUS_LIBS) \ $(AEGIS_LIBS) diff --git a/plugins/mcp-dbus-aegis-acl.c b/plugins/mcp-dbus-aegis-acl.c index 71d953c..9fd4957 100644 --- a/plugins/mcp-dbus-aegis-acl.c +++ b/plugins/mcp-dbus-aegis-acl.c @@ -1,5 +1,5 @@ /* - * An Aegis/libcreds plugin that checks the caller's permission tokens + * A pseudo-plugin that checks the caller's Aegis permission tokens * * Copyright © 2010-2011 Nokia Corporation * Copyright © 2010-2011 Collabora Ltd. @@ -19,11 +19,39 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" + +#ifdef G_LOG_DOMAIN +#undef G_LOG_DOMAIN +#endif +#define G_LOG_DOMAIN "mission-control-DBus-Access-ACL" + +#ifdef ENABLE_DEBUG +#define DEBUG(_f, ...) g_debug ("%s: " _f, G_STRLOC, ##__VA_ARGS__) +#else +#define DEBUG(_f, ...) do {} while (0) +#endif + +#include +#include +#include +#include + #include + #include #include -#include -#include + +typedef struct _AegisAcl AegisAcl; +typedef struct _AegisAclClass AegisAclClass; + +struct _AegisAcl { + GObject parent; +}; + +struct _AegisAclClass { + GObjectClass parent_class; +}; #define CREATE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".CreateChannel" #define ENSURE_CHANNEL TP_IFACE_CONNECTION_INTERFACE_REQUESTS ".EnsureChannel" @@ -32,7 +60,8 @@ #define AEGIS_CALL_TOKEN "Cellular" -#define DEBUG g_debug +/* implemented by the Aegis-patched dbus-daemon */ +#define AEGIS_INTERFACE "com.meego.DBus.Creds" #define PLUGIN_NAME "dbus-aegis-acl" #define PLUGIN_DESCRIPTION \ @@ -40,63 +69,52 @@ "associated with the calling process ID and determine whether " \ "the DBus call or property access should be allowed" -static gboolean token_initialised = FALSE; static creds_value_t aegis_token = CREDS_BAD; static creds_type_t aegis_type = CREDS_BAD; -static gchar *restricted[] = - { - CREATE_CHANNEL, - ENSURE_CHANNEL, - SEND_MESSAGE, - NULL - }; - -static void dbus_acl_iface_init (McpDBusAclIface *, +static void aegis_acl_iface_init (McpDBusAclIface *, + gpointer); +static void aegis_cdo_policy_iface_init (McpDispatchOperationPolicyIface *, gpointer); -typedef struct { - GObject parent; -} DBusAegisAcl; - -typedef struct { - GObjectClass parent_class; - creds_value_t token; - creds_type_t token_type; -} DBusAegisAclClass; - -GType dbus_aegis_acl_get_type (void) G_GNUC_CONST; - -#define DBUS_AEGIS_ACL(o) \ - (G_TYPE_CHECK_INSTANCE_CAST ((o), dbus_aegis_acl_get_type (), \ - DBusAegisAcl)) +static GType aegis_acl_get_type (void); -G_DEFINE_TYPE_WITH_CODE (DBusAegisAcl, dbus_aegis_acl, +G_DEFINE_TYPE_WITH_CODE (AegisAcl, aegis_acl, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, dbus_acl_iface_init)); + G_IMPLEMENT_INTERFACE (MCP_TYPE_DBUS_ACL, aegis_acl_iface_init); + G_IMPLEMENT_INTERFACE (MCP_TYPE_DISPATCH_OPERATION_POLICY, + aegis_cdo_policy_iface_init)) static void -dbus_aegis_acl_init (DBusAegisAcl *self) +aegis_acl_init (AegisAcl *self) { } static void -dbus_aegis_acl_class_init (DBusAegisAclClass *cls) +aegis_acl_class_init (AegisAclClass *cls) { - if (token_initialised == TRUE) + if (aegis_type != CREDS_BAD) return; aegis_type = creds_str2creds (AEGIS_CALL_TOKEN, &aegis_token); } +static gchar *restricted_methods[] = + { + CREATE_CHANNEL, + ENSURE_CHANNEL, + SEND_MESSAGE, + NULL + }; + static gboolean method_is_filtered (const gchar *method) { guint i; - for (i = 0; restricted[i] != NULL; i++) + for (i = 0; restricted_methods[i] != NULL; i++) { - if (!tp_strdiff (method, restricted[i])) + if (!tp_strdiff (method, restricted_methods[i])) return TRUE; } @@ -139,23 +157,76 @@ is_filtered (DBusAclType type, return FALSE; } +/* For simplicity we don't implement non-trivial conversion between + * dbus-glib's arrays of guint, and libcreds' arrays of uint32_t. + * If this assertion fails on your platform, you'll need to implement it. */ +G_STATIC_ASSERT (sizeof (guint) == sizeof (uint32_t)); + static gboolean -pid_is_permitted (const McpDBusAcl *self, pid_t pid) +caller_creds_are_enough (const gchar *name, + const GArray *au) { - gboolean ok = FALSE; + creds_t caller_creds = creds_import ((const uint32_t *) au->data, au->len); + gboolean ok = creds_have_p (caller_creds, aegis_type, aegis_token); - if (pid != 0) +#ifdef ENABLE_DEBUG + if (ok) + { + DEBUG ("Caller %s is appropriately privileged", name); + } + else { - creds_t caller = creds_gettask (pid); + char buf[1024]; + creds_type_t debug_type; + creds_value_t debug_value; + int i = 0; + + DEBUG ("Caller %s has these credentials:", name); + + while ((debug_type = creds_list (caller_creds, i, &debug_value)) + != CREDS_BAD) + { + creds_creds2str (debug_type, debug_value, buf, sizeof (buf)); + DEBUG ("- %s", buf); + } + + DEBUG ("but they are insufficient"); + } +#endif + + creds_free (caller_creds); + return ok; +} - DEBUG ("creds_have_p (creds_gettask (%d) -> %p, %d, %ld)", - pid, caller, aegis_type, aegis_token); - ok = creds_have_p (caller, aegis_type, aegis_token); - DEBUG (" --> %s", ok ? "TRUE" : "FALSE"); +static gboolean +check_peer_creds_sync (DBusGConnection *dgc, + const gchar *bus_name) +{ + DBusGProxy *proxy = dbus_g_proxy_new_for_name (dgc, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + AEGIS_INTERFACE); + GArray *au = NULL; + GError *error = NULL; + gboolean ok; - creds_free (caller); + if (dbus_g_proxy_call (proxy, "GetConnectionCredentials", &error, + G_TYPE_STRING, bus_name, + G_TYPE_INVALID, + DBUS_TYPE_G_UINT_ARRAY, &au, + G_TYPE_INVALID)) + { + ok = caller_creds_are_enough (bus_name, au); + g_array_unref (au); + } + else + { + DEBUG ("GetConnectionCredentials failed: %s", error->message); + g_clear_error (&error); + ok = FALSE; } + g_object_unref (proxy); return ok; } @@ -172,24 +243,11 @@ caller_authorised (const McpDBusAcl *self, if (is_filtered (type, name, params)) { - pid_t pid = 0; - GError *error = NULL; gchar *caller = dbus_g_method_get_sender ((DBusGMethodInvocation *) call); - DBusGProxy *proxy = dbus_g_proxy_new_for_name (dgc, - DBUS_SERVICE_DBUS, - DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); - - dbus_g_proxy_call (proxy, "GetConnectionUnixProcessID", &error, - G_TYPE_STRING, caller, - G_TYPE_INVALID, - G_TYPE_UINT, &pid, - G_TYPE_INVALID); - ok = pid_is_permitted (self, pid); + ok = check_peer_creds_sync (dgc, caller); g_free (caller); - g_object_unref (proxy); } DEBUG ("sync Aegis ACL check [%s]", ok ? "Allowed" : "Forbidden"); @@ -204,24 +262,30 @@ async_authorised_cb (DBusGProxy *proxy, { GError *error = NULL; DBusAclAuthData *ad = data; - pid_t pid = 0; + GArray *au = NULL; const McpDBusAcl *self = ad->acl; gboolean permitted = FALSE; - /* if this returns FALSE, there's no PID, which means something bizarre * - * and untrustowrthy is going on, which in turn means we must deny: can't * - * authorise without first authenticating */ + /* if this returns FALSE, there are no credentials, which means something + * untrustworthy is going on, which in turn means we must deny: can't + * authorise without first authenticating */ permitted = dbus_g_proxy_end_call (proxy, call, &error, - G_TYPE_UINT, &pid, + DBUS_TYPE_G_UINT_ARRAY, &au, G_TYPE_INVALID); if (permitted) - permitted = pid_is_permitted (self, pid); + { + permitted = caller_creds_are_enough (ad->name, au); + g_array_unref (au); + } else - g_error_free (error); + { + DEBUG ("GetConnectionCredentials failed: %s", error->message); + g_clear_error (&error); + } - DEBUG ("finished async Aegis ACL check [%u -> %s]", - pid, permitted ? "Allowed" : "Forbidden"); + DEBUG ("finished async Aegis ACL check [%s]", + permitted ? "Allowed" : "Forbidden"); mcp_dbus_acl_authorised_async_step (ad, permitted); @@ -244,9 +308,9 @@ caller_async_authorised (const McpDBusAcl *self, proxy = dbus_g_proxy_new_for_name (dgc, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, - DBUS_INTERFACE_DBUS); + AEGIS_INTERFACE); - dbus_g_proxy_begin_call (proxy, "GetConnectionUnixProcessID", + dbus_g_proxy_begin_call (proxy, "GetConnectionCredentials", async_authorised_cb, data, NULL, @@ -263,7 +327,7 @@ caller_async_authorised (const McpDBusAcl *self, static void -dbus_acl_iface_init (McpDBusAclIface *iface, +aegis_acl_iface_init (McpDBusAclIface *iface, gpointer unused G_GNUC_UNUSED) { mcp_dbus_acl_iface_set_name (iface, PLUGIN_NAME); @@ -273,18 +337,50 @@ dbus_acl_iface_init (McpDBusAclIface *iface, mcp_dbus_acl_iface_implement_authorised_async (iface, caller_async_authorised); } -GObject * -mcp_plugin_ref_nth_object (guint n) +static gchar *restricted_cms[] = { "ring", "mmscm", NULL }; + +static inline gboolean +cm_is_restricted (const gchar *cm_name) { - DEBUG ("Initializing mcp-dbus-caller-id plugin (n=%u)", n); + guint i; - switch (n) + for (i = 0; restricted_cms[i] != NULL; i++) { - case 0: - return g_object_new (dbus_aegis_acl_get_type (), NULL); + if (!tp_strdiff (restricted_cms[i], cm_name)) + return TRUE; + } + + return FALSE; +} - default: - return NULL; +static gboolean +handler_is_suitable (McpDispatchOperationPolicy *self, + TpProxy *recipient, + McpDispatchOperation *dispatch_op) +{ + gboolean ok = TRUE; + const gchar *manager = mcp_dispatch_operation_get_cm_name (dispatch_op); + + if (cm_is_restricted (manager)) + { + ok = check_peer_creds_sync (tp_proxy_get_dbus_connection (recipient), + tp_proxy_get_bus_name (recipient)); } + + DEBUG ("sync Aegis CDO policy check [%s]", ok ? "Allowed" : "Forbidden"); + + return ok; +} + +static void +aegis_cdo_policy_iface_init (McpDispatchOperationPolicyIface *iface, + gpointer unused G_GNUC_UNUSED) +{ + iface->handler_is_suitable = handler_is_suitable; } +GObject * +aegis_acl_new (void) +{ + return g_object_new (aegis_acl_get_type (), NULL); +} diff --git a/plugins/mcp-dbus-aegis-acl.h b/plugins/mcp-dbus-aegis-acl.h new file mode 100644 index 0000000..96baaf4 --- /dev/null +++ b/plugins/mcp-dbus-aegis-acl.h @@ -0,0 +1,34 @@ +/* + * A pseudo-plugin that checks the caller's Aegis permission tokens + * + * Copyright © 2010-2011 Nokia Corporation + * Copyright © 2010-2011 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AEGIS_ACL_H +#define AEGIS_ACL_H + +#include +#include + +G_BEGIN_DECLS + +GObject *aegis_acl_new (void); + +G_END_DECLS + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 860374c..dbc10d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -117,6 +117,10 @@ libmcd_convenience_la_LIBADD += $(LIBACCOUNTS_SSO_LIBS) INCLUDES += $(LIBACCOUNTS_SSO_CFLAGS) endif +if ENABLE_AEGIS +libmcd_convenience_la_LIBADD += $(top_builddir)/plugins/libmcp-aegis.la +endif + noinst_LTLIBRARIES = libmcd-convenience.la if ENABLE_MCD_PLUGINS diff --git a/src/mcd-dispatch-operation.c b/src/mcd-dispatch-operation.c index bcc31f5..3ae9390 100644 --- a/src/mcd-dispatch-operation.c +++ b/src/mcd-dispatch-operation.c @@ -2208,6 +2208,61 @@ _mcd_dispatch_operation_run_clients (McdDispatchOperation *self) } /* + * @dbus: a #TpDBusDaemon instance + * @recipient: the #TpProxy for the handler or observer + * @channels: a #GPtrArray of #TpChannel objects + * @denied: a place to store a #GError indicating why the handler was denied + * + * @denied should point to a GError * which is NULL, and will be set + * only if a plugin denies a handler permission to proceed. + * + * This method calls each #McpDispatchOperationPolicyIface plugin's + * @handler_is_suitable method. + * + * If any plugin returns %FALSE, the call is considered to be forbidden. + * (and no further plugins are invoked). + * + * Returns: a #gboolean - %TRUE for permitted, %FALSE for forbidden. + **/ +static gboolean +mcd_dispatch_operation_check_handler (McdDispatchOperation *self, + McdClientProxy *handler, + GError **denied) +{ + TpProxy *handler_proxy = (TpProxy *) handler; + const GList *p; + McpDispatchOperation *plugin_api = MCP_DISPATCH_OPERATION ( + self->priv->plugin_api); + + DEBUG ("%s: channel ACL verification [%u channels]", + self->priv->unique_name, + g_list_length (self->priv->channels)); + + for (p = mcp_list_objects (); p != NULL; p = g_list_next (p)) + { + if (MCP_IS_DISPATCH_OPERATION_POLICY (p->data)) + { + McpDispatchOperationPolicy *plugin = p->data; + + DEBUG ("%s: checking policy for %s", + G_OBJECT_TYPE_NAME (plugin), + tp_proxy_get_object_path (handler_proxy)); + + if (!mcp_dispatch_operation_policy_handler_is_suitable (plugin, + handler_proxy, plugin_api)) + { + g_set_error (denied, DBUS_GERROR, DBUS_GERROR_ACCESS_DENIED, + "permission denied by dispatch operation policy '%s'", + G_OBJECT_TYPE_NAME (plugin)); + return FALSE; + } + } + } + + return TRUE; +} + +/* * mcd_dispatch_operation_handle_channels: * @self: the dispatch operation * @handler: the selected handler @@ -2254,6 +2309,7 @@ _mcd_dispatch_operation_try_next_handler (McdDispatchOperation *self) * even if it already failed - perhaps the Approver is feeling lucky. */ if (approval != NULL && approval->client_bus_name != NULL) { + GError *error = NULL; McdClientProxy *handler = _mcd_client_registry_lookup ( self->priv->client_registry, approval->client_bus_name); gboolean failed = _mcd_dispatch_operation_get_handler_failed (self, @@ -2269,8 +2325,11 @@ _mcd_dispatch_operation_try_next_handler (McdDispatchOperation *self) if (handler != NULL && (approval->type == APPROVAL_TYPE_HANDLE_WITH || !failed)) { - mcd_dispatch_operation_handle_channels (self, handler); - return TRUE; + if (mcd_dispatch_operation_check_handler (self, handler, &error)) + { + mcd_dispatch_operation_handle_channels (self, handler); + return TRUE; + } } /* If the Handler has disappeared, a HandleWith call should fail, @@ -2278,11 +2337,16 @@ _mcd_dispatch_operation_try_next_handler (McdDispatchOperation *self) * can legitimately try more handlers. */ if (approval->type == APPROVAL_TYPE_HANDLE_WITH) { - GError gone = { TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, - "The requested Handler does not exist" }; - g_queue_pop_head (self->priv->approvals); - dbus_g_method_return_error (approval->context, &gone); + + if (error == NULL) + error = g_error_new_literal (TP_ERRORS, + TP_ERROR_NOT_IMPLEMENTED, + "The requested Handler does not exist"); + + dbus_g_method_return_error (approval->context, error); + g_error_free (error); + approval->context = NULL; approval_free (approval); return TRUE; @@ -2297,6 +2361,9 @@ _mcd_dispatch_operation_try_next_handler (McdDispatchOperation *self) self->priv->client_registry, *iter); gboolean failed = _mcd_dispatch_operation_get_handler_failed (self, *iter); + GError *error = NULL; + TpProxy *client = (TpProxy *) handler; + const gchar *name = tp_proxy_get_bus_name (client); DEBUG ("Possible handler: %s (still exists: %c, already failed: %c)", *iter, handler != NULL ? 'Y' : 'N', failed ? 'Y' : 'N'); @@ -2304,8 +2371,16 @@ _mcd_dispatch_operation_try_next_handler (McdDispatchOperation *self) if (handler != NULL && !failed && (is_approved || _mcd_client_proxy_get_bypass_approval (handler))) { - mcd_dispatch_operation_handle_channels (self, handler); - return TRUE; + if (mcd_dispatch_operation_check_handler (self, handler, &error)) + { + mcd_dispatch_operation_handle_channels (self, handler); + return TRUE; + } + else + { + DEBUG ("handler %s rejected by ACL: %s", name, error->message); + g_clear_error (&error); + } } } diff --git a/src/mcd-manager.c b/src/mcd-manager.c index d7911e9..9df13ce 100644 --- a/src/mcd-manager.c +++ b/src/mcd-manager.c @@ -220,10 +220,8 @@ _mcd_manager_constructor (GType type, guint n_params, { GObjectClass *object_class = (GObjectClass *)mcd_manager_parent_class; McdManager *manager; - McdManagerPrivate *priv; manager = MCD_MANAGER (object_class->constructor (type, n_params, params)); - priv = manager->priv; g_return_val_if_fail (manager != NULL, NULL); diff --git a/src/plugin-account.c b/src/plugin-account.c index 1680914..cfb83ab 100644 --- a/src/plugin-account.c +++ b/src/plugin-account.c @@ -303,6 +303,9 @@ sort_and_cache_plugins () const GList *p; static gboolean plugins_cached = FALSE; + if (plugins_cached) + return; + /* not guaranteed to have been called, but idempotent: */ _mcd_plugin_loader_init (); diff --git a/src/plugin-loader.c b/src/plugin-loader.c index 35e3625..7062c3a 100644 --- a/src/plugin-loader.c +++ b/src/plugin-loader.c @@ -25,6 +25,12 @@ #include +#include "mcd-debug.h" + +#if ENABLE_AEGIS +#include "plugins/mcp-dbus-aegis-acl.h" +#endif + static gsize ready = 0; void @@ -32,12 +38,25 @@ _mcd_plugin_loader_init (void) { if (g_once_init_enter (&ready)) { +#if ENABLE_AEGIS + GObject *pseudo_plugin; +#endif const gchar *dir = g_getenv ("MC_FILTER_PLUGIN_DIR"); if (dir == NULL) dir = MCD_PLUGIN_LOADER_DIR; mcp_read_dir (dir); + +#if ENABLE_AEGIS + /* The last object added by mcp_add_object() will be treated as highest + * priority, at least for the interfaces used here */ + DEBUG ("Initialising built-in Aegis ACL plugin"); + pseudo_plugin = G_OBJECT (aegis_acl_new ()); + mcp_add_object (pseudo_plugin); + g_object_unref (pseudo_plugin); +#endif + g_once_init_leave (&ready, 1); } } diff --git a/tests/twisted/mcp-plugin.c b/tests/twisted/mcp-plugin.c index 53aa173..1c31acb 100644 --- a/tests/twisted/mcp-plugin.c +++ b/tests/twisted/mcp-plugin.c @@ -183,8 +183,7 @@ static void cdo_policy_iface_init (McpDispatchOperationPolicyIface *iface, gpointer unused G_GNUC_UNUSED) { - mcp_dispatch_operation_policy_iface_implement_check (iface, - test_permission_plugin_check_cdo); + iface->check = test_permission_plugin_check_cdo; } typedef struct { @@ -378,8 +377,7 @@ static void rej_cdo_policy_iface_init (McpDispatchOperationPolicyIface *iface, gpointer unused G_GNUC_UNUSED) { - mcp_dispatch_operation_policy_iface_implement_check (iface, - test_rejection_plugin_check_cdo); + iface->check = test_rejection_plugin_check_cdo; } static void