From dd0b0d22d812b6a081ada59c7f636b8130ab2f11 Mon Sep 17 00:00:00 2001 From: Allison Lortie Date: Tue, 22 Nov 2016 16:24:01 +0100 Subject: Initial check-in of dconf-proxy code --- Makefile.am | 2 +- configure.ac | 1 + proxy/.gitignore | 2 + proxy/Makefile.am | 24 +++ proxy/confinement-flatpak.c | 219 ++++++++++++++++++++++++ proxy/confinement.c | 42 +++++ proxy/confinement.h | 32 ++++ proxy/permissions.c | 137 +++++++++++++++ proxy/permissions.h | 70 ++++++++ proxy/proxy.c | 397 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 925 insertions(+), 1 deletion(-) create mode 100644 proxy/.gitignore create mode 100644 proxy/Makefile.am create mode 100644 proxy/confinement-flatpak.c create mode 100644 proxy/confinement.c create mode 100644 proxy/confinement.h create mode 100644 proxy/permissions.c create mode 100644 proxy/permissions.h create mode 100644 proxy/proxy.c diff --git a/Makefile.am b/Makefile.am index dd3571a..1b9b004 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ include Makefile.gtester ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = shm gvdb common engine service gdbus gsettings client bin docs tests +SUBDIRS = shm gvdb common engine service proxy gdbus gsettings client bin docs tests DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc EXTRA_DIST = trim-lcov.py m4 diff --git a/configure.ac b/configure.ac index f2c3d46..8fbff08 100644 --- a/configure.ac +++ b/configure.ac @@ -86,6 +86,7 @@ AC_CONFIG_FILES([ client/Makefile service/Makefile bin/Makefile + proxy/Makefile tests/Makefile docs/Makefile Makefile diff --git a/proxy/.gitignore b/proxy/.gitignore new file mode 100644 index 0000000..8e87557 --- /dev/null +++ b/proxy/.gitignore @@ -0,0 +1,2 @@ +/dconf-proxy +/ca.desrt.dconf.Proxy.service diff --git a/proxy/Makefile.am b/proxy/Makefile.am new file mode 100644 index 0000000..7042bf4 --- /dev/null +++ b/proxy/Makefile.am @@ -0,0 +1,24 @@ +include $(top_srcdir)/Makefile.gtester + +libexec_PROGRAMS = dconf-proxy + +dbusservice_DATA = ca.desrt.dconf.Proxy.service + +dconf_proxy_CFLAGS = $(gio_CFLAGS) +dconf_proxy_LDADD = $(gio_LIBS) + +dconf_proxy_SOURCES = \ + confinement.c \ + confinement-flatpak.c \ + confinement.h \ + permissions.c \ + permissions.h \ + proxy.c + +DISTCLEANFILES = ca.desrt.dconf.service + +ca.desrt.dconf.Proxy.service: Makefile + $(AM_V_GEN) (echo '[D-BUS Service]'; \ + echo 'Name=ca.desrt.dconf.Proxy'; \ + echo 'Exec=${libexecdir}/dconf-proxy') > $@.tmp && \ + mv $@.tmp $@ diff --git a/proxy/confinement-flatpak.c b/proxy/confinement-flatpak.c new file mode 100644 index 0000000..6035783 --- /dev/null +++ b/proxy/confinement-flatpak.c @@ -0,0 +1,219 @@ +/* + * 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 . + * + * Author: Allison Lortie + */ + +#define _GNU_SOURCE + +#include "confinement.h" + +#include +#include +#include + +#include +#include +#include +#include + +static gboolean +verify_tmpfs (gint fd, + gboolean *out_is_tmpfs) +{ + struct statfs buf; + + if (fstatfs (fd, &buf) != 0) + return FALSE; + + *out_is_tmpfs = (buf.f_type == TMPFS_MAGIC); + + return TRUE; +} + +static gboolean +verify_regular (gint fd, + gboolean *out_is_regular, + gsize *out_size) +{ + struct stat buf; + + if (fstat (fd, &buf) != 0) + return FALSE; + + *out_is_regular = S_ISREG (buf.st_mode); + *out_size = buf.st_size; + + return TRUE; +} + +static void +fd_clear (gint *fd) +{ + if (*fd != -1) + close (*fd); +} + +typedef gint fd; +G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(fd, fd_clear) + +static gboolean +get_flatpak_info_keyfile (guint pid, + GKeyFile **out_keyfile) +{ + g_autoptr(GKeyFile) keyfile = NULL; + g_autofree gchar *contents = NULL; + g_autoptr(GError) error = NULL; + g_auto(fd) root_fd = -1; + g_auto(fd) info_fd = -1; + gboolean is_regular; + gboolean is_tmpfs; + gchar rootdir[40]; + gsize size; + + snprintf (rootdir, sizeof rootdir, "/proc/%u/root", pid); + root_fd = open (rootdir, O_DIRECTORY | O_PATH); + + if (root_fd == -1) + { + g_warning ("pid %u: cannot access root filesystem: %s", pid, g_strerror (errno)); + return FALSE; + } + + /* do you guarantee for always and forever that the root fs of a flatpak app is tmpfs? + * yes + */ + if (!verify_tmpfs (root_fd, &is_tmpfs)) + { + g_warning ("pid %u: fstatfs() on root filesystem failed: %s", pid, g_strerror (errno)); + return FALSE; + } + + if (!is_tmpfs) + { + /* Unconfined */ + + *out_keyfile = NULL; + return TRUE; + } + + info_fd = openat (root_fd, ".flatpak-info", O_RDONLY | O_NOCTTY); + if (info_fd == -1 && errno == ENOENT) + { + /* Unconfined */ + *out_keyfile = NULL; + return TRUE; + } + + /* This is now surely a flatpak-confined application. We only have two + * options past this point: failure, or returning a non-NULL GKeyFile. + */ + + if (info_fd == -1) + { + g_warning ("pid %u: failed to open .flatpak-info file: %s", pid, g_strerror (errno)); + return FALSE; + } + + if (!verify_regular (info_fd, &is_regular, &size)) + { + g_warning ("pid %u: fstat() on .flatpak-info file failed: %s", pid, g_strerror (errno)); + return FALSE; + } + + if (!is_regular) + { + g_warning ("pid %u: .flatpak-info is not regular file", pid); + return FALSE; + } + + if (size > 1000000) + { + g_warning ("pid %u: .flatpak-info file is unreasonably large", pid); + return FALSE; + } + + contents = g_malloc (size); + if (read (info_fd, contents, size) != size) + { + /* "No error" in this case means that the size changed */ + g_warning ("pid %u: failed to read entire .flatpak-info file: %s", pid, g_strerror (errno)); + return FALSE; + } + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_data (keyfile, contents, size, G_KEY_FILE_NONE, &error)) + { + g_warning ("pid %u: cannot parse .flatpak-info contents: %s", pid, error->message); + return FALSE; + } + + *out_keyfile = g_steal_pointer (&keyfile); + + return TRUE; +} + +gboolean +confinement_check_flatpak (GVariant *credentials, + gboolean *out_is_confined, + Permissions *out_permissions) +{ + g_autoptr(GKeyFile) keyfile = NULL; + g_autoptr(GError) error = NULL; + gchar *appid; + guint pid; + + if (!g_variant_lookup (credentials, "ProcessID", "u", &pid)) + { + g_warning ("Caller credentials are missing ProcessID field"); + return FALSE; + } + + if (!get_flatpak_info_keyfile (pid, &keyfile)) + /* this will throw its own g_warning() */ + return FALSE; + + if (keyfile == NULL) + { + /* Everything went OK, but we didn't find a keyfile there. As far + * as flatpak is concerned, this app is unconfined. + */ + *out_is_confined = FALSE; + return TRUE; + } + + appid = g_key_file_get_string (keyfile, "Application", "name", &error); + if (appid == NULL) + { + g_warning ("pid %u: .flatpak-info: %s", pid, error->message); + return FALSE; + } + + /* We will have success now, even if we don't find the dconf keys (in + * which case there is simply no permissions to access dconf and we + * share an empty database). + */ + permission_list_init (&out_permissions->readable, + g_key_file_get_string_list (keyfile, "Policy dconf", "readable", NULL, NULL)); + permission_list_init (&out_permissions->writable, + g_key_file_get_string_list (keyfile, "Policy dconf", "writable", NULL, NULL)); + out_permissions->ipc_dir = g_build_filename (g_get_user_runtime_dir (), "app", appid, NULL); + out_permissions->app_id = appid; + + *out_is_confined = TRUE; + + return TRUE; +} diff --git a/proxy/confinement.c b/proxy/confinement.c new file mode 100644 index 0000000..33c3059 --- /dev/null +++ b/proxy/confinement.c @@ -0,0 +1,42 @@ +/* + * 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 . + * + * Author: Allison Lortie + */ + +#include "confinement.h" + +gboolean +confinement_check (GVariant *credentials, + gboolean *out_is_confined, + Permissions *out_confined_permissions) +{ + Permissions permissions; + gboolean is_confined; + + if (!confinement_check_flatpak (credentials, &is_confined, &permissions)) + return FALSE; + + if (!is_confined) + /* snap goes here */; + + *out_is_confined = is_confined; + + if (is_confined) + *out_confined_permissions = permissions; + + return TRUE; +} diff --git a/proxy/confinement.h b/proxy/confinement.h new file mode 100644 index 0000000..9b6b625 --- /dev/null +++ b/proxy/confinement.h @@ -0,0 +1,32 @@ +/* + * 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 . + * + * Author: Allison Lortie + */ + +#pragma once + +#include "permissions.h" + +gboolean +confinement_check (GVariant *credentials, + gboolean *out_is_confined, + Permissions *out_permissions); + +gboolean +confinement_check_flatpak (GVariant *credentials, + gboolean *out_is_confined, + Permissions *out_permissions); diff --git a/proxy/permissions.c b/proxy/permissions.c new file mode 100644 index 0000000..3201126 --- /dev/null +++ b/proxy/permissions.c @@ -0,0 +1,137 @@ +/* + * 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 . + * + * Author: Allison Lortie + */ + +#include "permissions.h" + +void +permission_list_add (PermissionList *self, + const gchar *string) +{ + gsize current; + + current = (gsize) g_hash_table_lookup (self->hash_table, string); + g_hash_table_insert (self->hash_table, g_strdup (string), (gpointer) (current + 1)); +} + +void +permission_list_remove (PermissionList *self, + const gchar *string) +{ + gsize current; + + current = (gsize) g_hash_table_lookup (self->hash_table, string); + g_assert (current != 0); + + if (current > 1) + g_hash_table_insert (self->hash_table, g_strdup (string), (gpointer) (current - 1)); + else + g_hash_table_remove (self->hash_table, string); +} + +void +permission_list_merge (PermissionList *self, + PermissionList *to_merge) +{ + GHashTableIter iter; + gpointer key; + + g_hash_table_iter_init (&iter, to_merge->hash_table); + while (g_hash_table_iter_next (&iter, &key, NULL)) + permission_list_add (self, key); +} + +void +permission_list_unmerge (PermissionList *self, + PermissionList *to_unmerge) +{ + GHashTableIter iter; + gpointer key; + + g_hash_table_iter_init (&iter, to_unmerge->hash_table); + while (g_hash_table_iter_next (&iter, &key, NULL)) + permission_list_remove (self, key); +} + +void +permission_list_init (PermissionList *self, + gchar **contents) +{ + self->hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + if (contents != NULL) + { + gint i; + + for (i = 0; contents[i]; i++) + g_hash_table_insert (self->hash_table, contents[i], (gpointer) 1u); + + g_free (contents); + } +} + +void +permission_list_clear (PermissionList *self) +{ + g_hash_table_unref (self->hash_table); + self->hash_table = NULL; +} + +void +permissions_init (Permissions *permissions) +{ + permission_list_init (&permissions->readable, NULL); + permission_list_init (&permissions->writable, NULL); +} + +void +permissions_clear (Permissions *permissions) +{ + permission_list_clear (&permissions->readable); + permission_list_clear (&permissions->writable); +} + +static void +merge_string (gchar **dest, + const gchar *src) +{ + if (*dest == NULL) + *dest = g_strdup (src); + + g_assert_cmpstr (*dest, ==, src); +} + +void +permissions_merge (Permissions *permissions, + Permissions *to_merge) +{ + merge_string (&permissions->app_id, to_merge->app_id); + merge_string (&permissions->ipc_dir, to_merge->ipc_dir); + + permission_list_merge (&permissions->readable, &to_merge->readable); + permission_list_merge (&permissions->writable, &to_merge->writable); +} + +void +permissions_unmerge (Permissions *permissions, + Permissions *to_unmerge) +{ + permission_list_unmerge (&permissions->readable, &to_unmerge->readable); + permission_list_unmerge (&permissions->writable, &to_unmerge->writable); +} + diff --git a/proxy/permissions.h b/proxy/permissions.h new file mode 100644 index 0000000..aab0d3b --- /dev/null +++ b/proxy/permissions.h @@ -0,0 +1,70 @@ +/* + * 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 . + * + * Author: Allison Lortie + */ + +#pragma once + +#include + +typedef struct { + GHashTable *hash_table; +} PermissionList; + +typedef struct { + gchar *app_id; + gchar *ipc_dir; + PermissionList readable; + PermissionList writable; +} Permissions; + +void +permission_list_add (PermissionList *self, + const gchar *string); + +void +permission_list_remove (PermissionList *self, + const gchar *string); + +void +permission_list_merge (PermissionList *self, + PermissionList *to_merge); + +void +permission_list_unmerge (PermissionList *self, + PermissionList *to_unmerge); + +void +permission_list_init (PermissionList *self, + gchar **contents); + +void +permission_list_clear (PermissionList *self); + +void +permissions_init (Permissions *permissions); + +void +permissions_clear (Permissions *permissions); + +void +permissions_merge (Permissions *permissions, + Permissions *to_merge); + +void +permissions_unmerge (Permissions *permissions, + Permissions *to_unmerge); 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 . + * + * Author: Allison Lortie + */ + +#include "permissions.h" +#include "confinement.h" + +#include +#include + +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 ("" + "" + "" + "" + "" + "" + "" + "" + "", &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); +} -- cgit v1.2.1