[gnome-online-accounts/wip/rishi/identity-kernel-keyring-notification: 8/8] keyring notification



commit 022e99d4c8de75c989ec0aad95154973ea254252
Author: Debarshi Ray <debarshir gnome org>
Date:   Thu Jan 30 18:39:06 2020 +0100

    keyring notification

 configure.ac                                 |  12 ++
 meson.build                                  |  11 +-
 src/goaidentity/Makefile.am                  |   2 +
 src/goaidentity/goakerberosidentitymanager.c | 227 +++++++++++++++++++++++----
 src/goaidentity/meson.build                  |   3 +-
 5 files changed, 225 insertions(+), 30 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index a8517acc..f44f1fb9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,6 +31,12 @@ AX_COMPILER_FLAGS([WARN_CFLAGS],
                   [-Wno-cast-function-type -Wno-error=cast-function-type])
 
 AC_PROG_CC
+
+AC_PROG_CC_C99
+if test x$ac_cv_prog_cc_c99 = xno; then
+   AC_MSG_ERROR([C99-compatible compiler is needed])
+fi
+
 AC_PROG_LIBTOOL
 
 PKG_PROG_PKG_CONFIG(0.16)
@@ -411,6 +417,12 @@ fi
 
 AM_CONDITIONAL(BUILD_IDENTITY_SERVICE, [test x$enable_fedora != xno || test x$enable_kerberos != xno])
 
+PKG_CHECK_MODULES([LIBKEYUTILS], [libkeyutils >= 1.6.2])
+AC_SUBST(LIBKEYUTILS_CFLAGS)
+AC_SUBST(LIBKEYUTILS_LIBS)
+
+AC_CHECK_FUNCS(pipe2)
+
 # Optional timerfd support
 AC_MSG_CHECKING([for timerfd support])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
diff --git a/meson.build b/meson.build
index eb5eaa17..a79146e7 100644
--- a/meson.build
+++ b/meson.build
@@ -2,7 +2,10 @@ project(
   'gnome-online-accounts', 'c',
   version: '3.35.3',
   license: 'LGPL2+',
-  default_options: 'buildtype=debugoptimized',
+  default_options: [
+    'buildtype=debugoptimized',
+    'c_std=gnu99',
+  ]
   meson_version: '>= 0.50.0'
 )
 
@@ -137,6 +140,7 @@ enable_fedora = get_option('fedora')
 if enable_fedora
   gcr_dep = dependency('gcr-3')
   krb5_dep = dependency('krb5')
+  libkeyutils_dep = dependency('libkeyutils', version: '>= 1.6.2')
 
   config_h.set('GCR_API_SUBJECT_TO_CHANGE', true)
 endif
@@ -188,6 +192,7 @@ enable_kerberos = get_option('kerberos')
 if enable_kerberos
   gcr_dep = dependency('gcr-3')
   krb5_dep = dependency('krb5')
+  libkeyutils_dep = dependency('libkeyutils', version: '>= 1.6.2')
 
   config_h.set('GCR_API_SUBJECT_TO_CHANGE', true)
 endif
@@ -225,6 +230,10 @@ config_h.set_quoted('GOA_WINDOWS_LIVE_CLIENT_ID', windows_live_client_id)
 enable_windows_live = get_option('windows_live')
 config_h.set('GOA_WINDOWS_LIVE_ENABLED', enable_windows_live)
 
+if cc.has_function('pipe2')
+  config_h.set('HAVE_PIPE2', true)
+endif
+
 # Optional timerfd support
 timerfd_support_src = '''
   #include <sys/timerfd.h>
diff --git a/src/goaidentity/Makefile.am b/src/goaidentity/Makefile.am
index da09a0e4..6871ab00 100644
--- a/src/goaidentity/Makefile.am
+++ b/src/goaidentity/Makefile.am
@@ -91,6 +91,7 @@ goa_identity_service_CFLAGS =                                 \
        $(GLIB_CFLAGS)                                          \
        $(GTK_CFLAGS)                                           \
        $(KRB5_CFLAGS)                                          \
+       $(LIBKEYUTILS_CFLAGS)                                   \
        $(GCR_CFLAGS)                                           \
        $(NULL)
 
@@ -99,6 +100,7 @@ goa_identity_service_LDADD =                                         \
        $(GLIB_LIBS)                                            \
        $(GTK_LIBS)                                             \
        $(KRB5_LIBS)                                            \
+       $(LIBKEYUTILS_LIBS)                                     \
        $(GCR_LIBS)                                             \
        $(NULL)
 
diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c
index 678db4c6..7c339f94 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -18,20 +18,31 @@
 
 #include "config.h"
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+
 #include "goakerberosidentitymanager.h"
 #include "goaidentitymanager.h"
 #include "goaidentitymanagererror.h"
 #include "goaidentitymanagerprivate.h"
 #include "goakerberosidentityinquiry.h"
 
+#include <errno.h>
 #include <fcntl.h>
+#include <keyutils.h>
 #include <string.h>
+#include <unistd.h>
+#include <linux/watch_queue.h>
+#include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
+#include <glib-unix.h>
 #include <gio/gio.h>
+#include <gio/gunixinputstream.h>
 
 #include <krb5.h>
 
@@ -55,6 +66,7 @@ struct _GoaKerberosIdentityManager
 
   volatile int pending_refresh_count;
 
+  guint credentials_cache_keyring_notification_id;
   guint credentials_cache_polling_timeout_id;
 };
 
@@ -120,6 +132,17 @@ G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager,
                                                 initable_interface_init));
 #define FALLBACK_POLLING_INTERVAL 5
 
+enum
+{
+  WATCH_QUEUE_BUFFER_SIZE = 256
+};
+
+static const struct watch_notification_filter watch_queue_notification_filter =
+{
+  .nr_filters = 1,
+  .filters = { [0] = { .type = WATCH_TYPE_KEY_NOTIFY, .info_filter = 0, .info_mask = 0, .subtype_filter[0] = 
G_MAXUINT } }
+};
+
 static Operation *
 operation_new (GoaKerberosIdentityManager *self,
                GCancellable               *cancellable,
@@ -174,6 +197,85 @@ operation_free (Operation *operation)
   g_slice_free (Operation, operation);
 }
 
+static GSource *
+goa_kerberos_identity_manager_keyring_source_new (GError **error)
+{
+  GSource *ret = NULL;
+
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+#ifdef HAVE_PIPE2
+  {
+    g_autoptr (GInputStream) stream = NULL;
+    gint error_code;
+    gint fds[2] = { -1, -1 };
+
+    error_code = pipe2 (fds, O_NOTIFICATION_PIPE);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    close (fds[1]);
+
+    error_code = ioctl (fds[0], IOC_WATCH_QUEUE_SET_SIZE, WATCH_QUEUE_BUFFER_SIZE);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = ioctl(fds[0], IOC_WATCH_QUEUE_SET_FILTER, &watch_queue_notification_filter);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = keyctl_watch_key (KEY_SPEC_SESSION_KEYRING, fds[0], 0x01);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    error_code = keyctl_watch_key (KEY_SPEC_USER_KEYRING, fds[0], 0x02);
+    if (error_code == -1)
+      {
+        const gchar *error_str;
+
+        error_str = g_strerror (errno);
+        g_set_error_literal (error, G_UNIX_ERROR, 0, error_str);
+        goto out;
+      }
+
+    stream = g_unix_input_stream_new (fds[0], TRUE);
+    ret = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (stream), NULL);
+  }
+#else
+  {
+    g_set_error_literal (error, G_UNIX_ERROR, 0, "pipe2(2) not supported");
+    goto out;
+  }
+#endif
+
+ out:
+  return ret;
+}
+
 typedef struct {
   GSourceFunc func;
   gboolean ret_val;
@@ -1320,6 +1422,41 @@ credentials_cache_file_monitor_changed (GFileMonitor               *monitor,
   schedule_refresh (self);
 }
 
+static gboolean
+credentials_cache_keyring_notification (GPollableInputStream *stream, GoaKerberosIdentityManager *self)
+{
+  gssize bytes_read;
+  guchar buffer[433];
+
+  {
+    g_autoptr (GError) error = NULL;
+
+    bytes_read = g_pollable_input_stream_read_nonblocking (stream, buffer, sizeof (buffer), NULL, &error);
+    if (error != NULL)
+      {
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+          g_debug ("GoaKerberosIdentityManager: Keyring notification source not yet ready");
+        else
+          g_warning ("GoaKerberosIdentityManager: Could not read keyring notification source: %s", 
error->message);
+
+        goto out;
+      }
+  }
+
+  if (bytes_read != (gssize) sizeof (buffer))
+    {
+      g_warning ("GoaKerberosIdentityManager: Expected to read %" G_GSIZE_FORMAT " bytes, "
+                 "but received only %" G_GSSIZE_FORMAT " bytes",
+                 sizeof (buffer),
+                 bytes_read);
+    }
+
+  schedule_refresh (self);
+
+ out:
+  return G_SOURCE_CONTINUE;
+}
+
 static gboolean
 credentials_cache_polling_timeout (GoaKerberosIdentityManager *self)
 {
@@ -1335,7 +1472,6 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   krb5_ccache default_cache;
   const char *cache_type;
   const char *cache_path;
-  GFileMonitor *monitor = NULL;
   krb5_error_code error_code;
   GError *monitoring_error = NULL;
   gboolean can_monitor = TRUE;
@@ -1359,13 +1495,6 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   cache_type = krb5_cc_get_type (self->kerberos_context, default_cache);
   g_assert (cache_type != NULL);
 
-  if (strcmp (cache_type, "FILE") != 0 && strcmp (cache_type, "DIR") != 0)
-    {
-      g_warning ("GoaKerberosIdentityManager: Using polling for change notification for credential cache 
type '%s'",
-                 cache_type);
-      can_monitor = FALSE;
-    }
-
   g_free (self->credentials_cache_type);
   self->credentials_cache_type = g_strdup (cache_type);
 
@@ -1387,56 +1516,92 @@ monitor_credentials_cache (GoaKerberosIdentityManager  *self,
   if (cache_path[0] == ':')
     cache_path++;
 
-  if (can_monitor)
+  if (strcmp (cache_type, "FILE") == 0 || strcmp (cache_type, "DIR") == 0)
     {
-      GFile *file;
+      GFile *file = NULL;
+      GFileMonitor *monitor = NULL;
 
       file = g_file_new_for_path (cache_path);
 
-      monitoring_error = NULL;
       if (strcmp (cache_type, "FILE") == 0)
         {
-          monitor = g_file_monitor_file (file,
-                                         G_FILE_MONITOR_NONE,
-                                         NULL,
-                                         &monitoring_error);
+          monitoring_error = NULL;
+          monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &monitoring_error);
+          if (monitoring_error != NULL)
+            {
+              g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), 
reverting to "
+                         "polling: %s",
+                         cache_path,
+                         cache_type,
+                         monitoring_error->message);
+
+              can_monitor = FALSE;
+              g_error_free (monitoring_error);
+            }
         }
       else if (strcmp (cache_type, "DIR") == 0)
         {
           GFile *directory;
 
           directory = g_file_get_parent (file);
-          monitor = g_file_monitor_directory (directory,
-                                              G_FILE_MONITOR_NONE,
-                                              NULL,
-                                              &monitoring_error);
+
+          monitoring_error = NULL;
+          monitor = g_file_monitor_directory (directory, G_FILE_MONITOR_NONE, NULL, &monitoring_error);
+          if (monitoring_error != NULL)
+            {
+              g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), 
reverting to "
+                         "polling: %s",
+                         cache_path,
+                         cache_type,
+                         monitoring_error->message);
+
+              can_monitor = FALSE;
+              g_error_free (monitoring_error);
+            }
+
           g_object_unref (directory);
+        }
 
+      if (monitor != NULL)
+        {
+          g_signal_connect (G_OBJECT (monitor), "changed", G_CALLBACK 
(credentials_cache_file_monitor_changed), self);
+          self->credentials_cache_file_monitor = monitor;
         }
+
       g_object_unref (file);
     }
-
-  if (monitor == NULL)
+  else if (strcmp (cache_type, "KEYRING") == 0)
     {
+      GSource *keyring_source = NULL;
+
+      monitoring_error = NULL;
+      keyring_source = goa_kerberos_identity_manager_keyring_source_new (&monitoring_error);
       if (monitoring_error != NULL)
         {
-          g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), reverting 
to "
-                     "polling: %s",
+          g_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), reverting 
to polling: %s",
                      cache_path,
                      cache_type,
-                     monitoring_error != NULL? monitoring_error->message : "");
-          g_clear_error (&monitoring_error);
+                     monitoring_error->message);
+
+          can_monitor = FALSE;
+          g_error_free (monitoring_error);
         }
-      can_monitor = FALSE;
+
+      g_source_set_callback (keyring_source, (GSourceFunc) credentials_cache_keyring_notification, self, 
NULL);
+      self->credentials_cache_keyring_notification_id = g_source_attach (keyring_source, NULL);
+
+      g_clear_pointer (&keyring_source, g_source_unref);
     }
   else
     {
-      g_signal_connect (G_OBJECT (monitor), "changed", G_CALLBACK (credentials_cache_file_monitor_changed), 
self);
-      self->credentials_cache_file_monitor = monitor;
+      can_monitor = FALSE;
     }
 
   if (!can_monitor)
     {
+      g_warning ("GoaKerberosIdentityManager: Using polling for change notification for credential cache 
type '%s'",
+                 cache_type);
+
       self->credentials_cache_polling_timeout_id = g_timeout_add_seconds (FALLBACK_POLLING_INTERVAL,
                                                                           (GSourceFunc) 
credentials_cache_polling_timeout,
                                                                           self);
@@ -1458,6 +1623,12 @@ stop_watching_credentials_cache (GoaKerberosIdentityManager *self)
       g_clear_object (&self->credentials_cache_file_monitor);
     }
 
+  if (self->credentials_cache_keyring_notification_id != 0)
+    {
+      g_source_remove (self->credentials_cache_keyring_notification_id);
+      self->credentials_cache_keyring_notification_id = 0;
+    }
+
   if (self->credentials_cache_polling_timeout_id != 0)
     {
       g_source_remove (self->credentials_cache_polling_timeout_id);
diff --git a/src/goaidentity/meson.build b/src/goaidentity/meson.build
index 0cd6ad1f..697b5068 100644
--- a/src/goaidentity/meson.build
+++ b/src/goaidentity/meson.build
@@ -53,7 +53,8 @@ sources += gnome.mkenums(
 deps = [
   gcr_dep,
   krb5_dep,
-  libgoa_dep
+  libgoa_dep,
+  libkeyutils_dep,
 ]
 
 cflags = [


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