summaryrefslogtreecommitdiff
path: root/proxy/proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'proxy/proxy.c')
-rw-r--r--proxy/proxy.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/proxy/proxy.c b/proxy/proxy.c
new file mode 100644
index 0000000..04b6ea1
--- /dev/null
+++ b/proxy/proxy.c
@@ -0,0 +1,397 @@
+/*
+ * Copyright © 2016 Canonical Limited
+ *
+ * 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 of the licence, 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt@desrt.ca>
+ */
+
+#include "permissions.h"
+#include "confinement.h"
+
+#include <stdio.h>
+#include <gio/gio.h>
+
+typedef struct {
+ Permissions permissions;
+ gint ref_count;
+ gchar *node;
+} Application;
+
+typedef struct {
+ gchar *unique_name;
+ Permissions permissions;
+ guint watch_id;
+ Application *application;
+} ConfinedSender;
+
+typedef struct
+{
+ GHashTable *applications_by_id;
+ GHashTable *applications_by_node;
+ GHashTable *confined_senders_by_name;
+} DConfProxy;
+
+static DConfProxy *
+dconf_proxy_get (void)
+{
+ static DConfProxy *the_proxy;
+
+ if (the_proxy == NULL)
+ {
+ the_proxy = g_slice_new (DConfProxy);
+ the_proxy->applications_by_id = g_hash_table_new (g_str_hash, g_str_equal);
+ the_proxy->applications_by_node = g_hash_table_new (g_str_hash, g_str_equal);
+ the_proxy->confined_senders_by_name = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ return the_proxy;
+}
+
+static void
+confined_sender_vanished (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ ConfinedSender *self = user_data;
+
+ g_assert_cmpstr (name, ==, self->unique_name);
+
+ /* TODO: lookup the application and unmerge/unref */
+
+ permissions_clear (&self->permissions);
+ g_bus_unwatch_name (self->watch_id);
+ g_free (self->unique_name);
+
+ g_slice_free (ConfinedSender, self);
+}
+
+static void
+dconf_proxy_method_call (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+}
+
+static GVariant *
+dconf_proxy_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ Application *application = user_data;
+
+ g_assert_cmpstr (interface_name, ==, "ca.desrt.dconf.Proxy");
+ g_assert_cmpstr (property_name, ==, "Directory");
+
+ return g_variant_new_string (application->permissions.ipc_dir);
+}
+
+static gchar *
+dconf_proxy_create_node_name (const gchar *id)
+{
+ static int x;
+
+ return g_strdup_printf ("%d", x++);
+}
+
+static Application *
+dconf_proxy_get_application (DConfProxy *self,
+ const gchar *id)
+{
+ Application *application;
+
+ application = g_hash_table_lookup (self->applications_by_id, id);
+
+ if (application == NULL)
+ {
+ application = g_slice_new (Application);
+
+ permissions_init (&application->permissions);
+ application->permissions.app_id = g_strdup (id);
+ application->node = dconf_proxy_create_node_name (id);
+ application->ref_count = 0;
+
+ g_hash_table_insert (self->applications_by_id, application->permissions.app_id, application);
+ g_hash_table_insert (self->applications_by_node, application->node, application);
+ }
+
+ application->ref_count++;
+
+ return application;
+}
+
+static gboolean
+dconf_proxy_get_confined_sender (DConfProxy *self,
+ GDBusConnection *connection,
+ const gchar *sender,
+ ConfinedSender **out_confined_sender)
+{
+ g_autoptr(GVariant) reply, credentials;
+ ConfinedSender *confined_sender;
+ Permissions permissions;
+ gboolean is_confined;
+
+ g_assert (g_dbus_is_unique_name (sender));
+
+ reply = g_dbus_connection_call_sync (connection,
+ "org.freedesktop.DBus", "/",
+ "org.freedesktop.DBus", "GetConnectionCredentials",
+ g_variant_new ("(s)", sender),
+ G_VARIANT_TYPE ("(a{sv})"),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL);
+
+ if (reply == NULL)
+ return FALSE;
+
+ credentials = g_variant_get_child_value (reply, 0);
+
+ if (!confinement_check (credentials, &is_confined, &permissions))
+ return FALSE;
+
+ if (!is_confined)
+ {
+ *out_confined_sender = NULL;
+ return TRUE;
+ }
+
+ confined_sender = g_slice_new (ConfinedSender);
+ confined_sender->unique_name = g_strdup (sender);
+ confined_sender->permissions = permissions;
+ confined_sender->application = dconf_proxy_get_application (self, permissions.app_id);
+ confined_sender->watch_id = g_bus_watch_name_on_connection (connection, sender, G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL, confined_sender_vanished,confined_sender, NULL);
+
+ permissions_merge (&confined_sender->application->permissions, &confined_sender->permissions);
+
+ g_hash_table_insert (self->confined_senders_by_name, confined_sender->unique_name, confined_sender);
+
+ *out_confined_sender = confined_sender;
+
+ return TRUE;
+}
+
+static gboolean
+dconf_proxy_check_permissions (DConfProxy *self,
+ GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *node,
+ Application **out_application)
+{
+ ConfinedSender *confined_sender;
+ Application *application;
+
+ /* Find out if we have a confined sender. */
+ if (!dconf_proxy_get_confined_sender (self, connection, sender, &confined_sender))
+ return FALSE;
+
+ if (confined_sender)
+ {
+ /* The only thing we are allowed to return here is the application
+ * that belongs to this confined sender, but in case the node was
+ * specified, we need to verify that it was the correct one, too.
+ *
+ * We can skip the hash table lookup here because we already have
+ * the node string accessible directly.
+ */
+ if (node && !g_str_equal (node, confined_sender->application->node))
+ return FALSE;
+
+ application = confined_sender->application;
+ }
+ else
+ {
+ /* Unconfined sender. Lookup the application by the node ID, if
+ * we have it, otherwise return NULL.
+ */
+ if (node != NULL)
+ application = g_hash_table_lookup (self->applications_by_node, node);
+ else
+ application = NULL;
+ }
+
+ *out_application = application;
+
+ return TRUE;
+}
+
+static gchar **
+dconf_proxy_subtree_enumerate (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ gpointer user_data)
+{
+ DConfProxy *proxy = user_data;
+ Application *application;
+ gchar **result;
+
+ g_assert_cmpstr (object_path, ==, "/ca/desrt/dconf/Proxy");
+
+ /* Security check */
+ if (!dconf_proxy_check_permissions (proxy, connection, sender, NULL, &application))
+ return NULL;
+
+ if (application != NULL)
+ {
+ /* Specific confined application making the request */
+ result = g_new (gchar *, 1 + 1);
+ result[0] = g_strdup (application->node);
+ result[1] = NULL;
+ }
+ else
+ {
+ /* Unconfined caller: list all existing nodes (ie: debugging) */
+ gint i;
+
+ result = (gchar **) g_hash_table_get_keys_as_array (proxy->applications_by_node, NULL);
+ for (i = 0; result[i]; i++)
+ result[i] = g_strdup (result[i]);
+ }
+
+ return result;
+}
+
+static GDBusInterfaceInfo **
+dconf_proxy_subtree_introspect (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *node,
+ gpointer user_data)
+{
+ static GDBusInterfaceInfo *proxy_interface;
+ DConfProxy *proxy = user_data;
+ GDBusInterfaceInfo **result;
+ Application *application;
+
+ /* GDBus bug: g_assert (g_str_equal (object_path, "/ca/desrt/dconf/Proxy")); */
+
+ /* The root node has nothing on it */
+ if (node == NULL)
+ return NULL;
+
+ /* Do the permissions check */
+ if (!dconf_proxy_check_permissions (proxy, connection, sender, node, &application))
+ return NULL;
+
+ /* If we didn't find an application, we act as if there is no object */
+ if (application == NULL)
+ return NULL;
+
+ /* Prepare the blob */
+ if (proxy_interface == NULL)
+ {
+ GDBusNodeInfo *node_info;
+ GError *error = NULL;
+
+ node_info = g_dbus_node_info_new_for_xml ("<node>"
+ "<interface name='ca.desrt.dconf.Proxy'>"
+ "<property name='Directory' type='s' access='read'/>"
+ "<method name='Start'/>"
+ "<method name='Write'>"
+ "<arg direction='in' type='ay'/>"
+ "</method>"
+ "</interface>"
+ "</node>", &error);
+ g_assert_no_error (error);
+
+ proxy_interface = g_dbus_interface_info_ref (node_info->interfaces[0]);
+ g_dbus_node_info_unref (node_info);
+ }
+
+ result = g_new (GDBusInterfaceInfo *, 1 + 1);
+ result[0] = g_dbus_interface_info_ref (proxy_interface);
+ result[1] = NULL;
+
+ return result;
+}
+
+static const GDBusInterfaceVTable *
+dconf_proxy_subtree_dispatch (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *node,
+ gpointer *out_user_data,
+ gpointer user_data)
+{
+ static const GDBusInterfaceVTable vtable = {
+ .method_call = dconf_proxy_method_call,
+ .get_property = dconf_proxy_get_property
+ };
+ DConfProxy *proxy = user_data;
+ Application *application;
+
+ g_assert (g_str_equal (object_path, "/ca/desrt/dconf/Proxy"));
+
+ if (!dconf_proxy_check_permissions (proxy, connection, sender, node, &application))
+ return NULL;
+
+ if (application == NULL)
+ return NULL;
+
+ *out_user_data = application;
+
+ return &vtable;
+}
+
+static void
+dconf_proxy_bus_acquired_handler (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ const GDBusSubtreeVTable subtree_vtable = {
+ .enumerate = dconf_proxy_subtree_enumerate,
+ .introspect = dconf_proxy_subtree_introspect,
+ .dispatch = dconf_proxy_subtree_dispatch
+ };
+ DConfProxy *proxy = user_data;
+ GError *error = NULL;
+
+ g_dbus_connection_register_subtree (connection, "/ca/desrt/dconf/Proxy", &subtree_vtable,
+ G_DBUS_SUBTREE_FLAGS_DISPATCH_TO_UNENUMERATED_NODES,
+ proxy, NULL, &error);
+ g_assert_no_error (error);
+}
+
+static void
+dconf_proxy_name_lost_handler (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ g_error ("Unable to acquire bus name: %s. Exiting.", name);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ DConfProxy *proxy;
+
+ proxy = dconf_proxy_get ();
+
+ g_bus_own_name (G_BUS_TYPE_SESSION, "ca.desrt.dconf.Proxy", G_BUS_NAME_OWNER_FLAGS_NONE,
+ dconf_proxy_bus_acquired_handler, NULL, dconf_proxy_name_lost_handler,
+ proxy, NULL);
+
+ while (TRUE)
+ g_main_context_iteration (NULL, TRUE);
+}