summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDebarshi Ray <debarshir@gnome.org>2020-01-30 18:39:06 +0100
committerDebarshi Ray <debarshir@gnome.org>2020-02-08 21:35:49 +0100
commitac02930a7b71469fa951b74c83b052b1cbc2c347 (patch)
treeccc77315b6768e21356d9f662c862f6fa7cf79a2
parenta133a916c10003ae9d53f433627f005302c09c36 (diff)
downloadgnome-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.ac12
-rw-r--r--meson.build11
-rw-r--r--src/goaidentity/Makefile.am2
-rw-r--r--src/goaidentity/goakerberosidentitymanager.c163
-rw-r--r--src/goaidentity/meson.build3
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 = [