[dconf/wip/proxy: 11/12] Initial check-in of dconf-proxy code



commit dd0b0d22d812b6a081ada59c7f636b8130ab2f11
Author: Allison Lortie <desrt desrt ca>
Date:   Tue Nov 22 16:24:01 2016 +0100

    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 deletions(-)
---
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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt desrt ca>
+ */
+
+#define _GNU_SOURCE
+
+#include "confinement.h"
+
+#include <linux/magic.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+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;
+    }
+
+  /* <desrt> do you guarantee for always and forever that the root fs of a flatpak app is tmpfs?
+   * <alexlarsson> 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt desrt ca>
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt desrt ca>
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt desrt ca>
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ *
+ * Author: Allison Lortie <desrt desrt ca>
+ */
+
+#pragma once
+
+#include <glib.h>
+
+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 <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);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]