diff options
author | Debarshi Ray <debarshir@gnome.org> | 2020-01-30 18:39:06 +0100 |
---|---|---|
committer | Debarshi Ray <debarshir@gnome.org> | 2020-02-08 21:35:49 +0100 |
commit | ac02930a7b71469fa951b74c83b052b1cbc2c347 (patch) | |
tree | ccc77315b6768e21356d9f662c862f6fa7cf79a2 | |
parent | a133a916c10003ae9d53f433627f005302c09c36 (diff) | |
download | gnome-online-accounts-wip/rishi/identity-kernel-keyring-notification.tar.gz |
kerberos-identity-manager: Use Linux's notification pipe for KEYRINGwip/rishi/identity-kernel-keyring-notification
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
-rw-r--r-- | configure.ac | 12 | ||||
-rw-r--r-- | meson.build | 11 | ||||
-rw-r--r-- | src/goaidentity/Makefile.am | 2 | ||||
-rw-r--r-- | src/goaidentity/goakerberosidentitymanager.c | 163 | ||||
-rw-r--r-- | src/goaidentity/meson.build | 3 |
5 files changed, 189 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac index a8517ac..f44f1fb 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 eb5eaa1..0ef95ca 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 9273e80..e729220 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 3fab0f2..afd534f 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; @@ -1321,6 +1421,41 @@ credentials_cache_file_monitor_changed (GFileMonitor *monitor, } 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) { schedule_refresh (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 dae94d5..8be9771 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 = [ |