[gnome-online-accounts/wip/rishi/identity-kernel-keyring-notification: 2/2] keyring notification
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-online-accounts/wip/rishi/identity-kernel-keyring-notification: 2/2] keyring notification
- Date: Fri, 7 Feb 2020 19:01:08 +0000 (UTC)
commit 4e00fa5c3dcf9a80a2b6c011b10ef964949d5dae
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..0ef95cad 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 9273e80e..e729220f 100644
--- a/src/goaidentity/Makefile.am
+++ b/src/goaidentity/Makefile.am
@@ -93,6 +93,7 @@ goa_identity_service_CFLAGS = \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
$(KRB5_CFLAGS) \
+ $(LIBKEYUTILS_CFLAGS) \
$(GCR_CFLAGS) \
$(NULL)
@@ -101,6 +102,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..fb3c1d12 100644
--- a/src/goaidentity/goakerberosidentitymanager.c
+++ b/src/goaidentity/goakerberosidentitymanager.c
@@ -18,19 +18,30 @@
#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 "goalinuxnotificationstream.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 <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 = goa_linux_notification_stream_new (fds[0], fds[1]);
+ 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 dae94d5a..8be97713 100644
--- a/src/goaidentity/meson.build
+++ b/src/goaidentity/meson.build
@@ -55,7 +55,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]