[gnome-online-accounts/wip/rishi/identity-kernel-keyring-notification: 2/2] kerberos-identity-manager: Use Linux's notification pipe for KEYRING
- 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] kerberos-identity-manager: Use Linux's notification pipe for KEYRING
- Date: Sat, 8 Feb 2020 20:29:26 +0000 (UTC)
commit 0e47a0014eb14881555bac79fa05b6c3f152240b
Author: Debarshi Ray <debarshir gnome org>
Date: Thu Jan 30 18:39:06 2020 +0100
kerberos-identity-manager: Use Linux's notification pipe for KEYRING
So far, goa-identity-service didn't have a way to be notified about
changes to Kerberos credentials caches stored in the Linux kernel's
keyring. It resorted to polling the credentials caches to detect any
changes made to them, leading to higher power consumption.
The Linux kernel now offers a notification mechanism in the form of a
special pipe that can be used to listen for changes to the keyring.
This reduces the need to poll the credentials caches.
https://gitlab.gnome.org/GNOME/gnome-online-accounts/merge_requests/47
configure.ac | 12 ++
meson.build | 11 +-
src/goaidentity/Makefile.am | 2 +
src/goaidentity/goakerberosidentitymanager.c | 163 +++++++++++++++++++++++++++
src/goaidentity/meson.build | 3 +-
5 files changed, 189 insertions(+), 2 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 3fab0f2c..afd534f1 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,83 @@ 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;
+ }
+
+ 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 +1420,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)
{
@@ -1433,6 +1568,28 @@ monitor_credentials_cache (GoaKerberosIdentityManager *self,
g_object_unref (file);
}
+ 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",
+ cache_path,
+ cache_type,
+ monitoring_error->message);
+
+ can_monitor = FALSE;
+ g_error_free (monitoring_error);
+ }
+
+ 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
{
can_monitor = FALSE;
@@ -1464,6 +1621,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]