diff --git a/.gitignore b/.gitignore index a835c84..8e42bcc 100644 --- a/.gitignore +++ b/.gitignore @@ -128,3 +128,4 @@ /gnome-online-accounts-3.45.2.tar.xz /gnome-online-accounts-3.46.0.tar.xz /gnome-online-accounts-3.47.1.tar.xz +/gnome-online-accounts-3.48.0.tar.xz diff --git a/gnome-online-accounts.spec b/gnome-online-accounts.spec index cc0f4bb..e3a9f4a 100644 --- a/gnome-online-accounts.spec +++ b/gnome-online-accounts.spec @@ -5,19 +5,18 @@ %global webkit2gtk_version 2.33.1 Name: gnome-online-accounts -Version: 3.47.1 -Release: 4%{?dist} +Version: 3.48.0 +Release: 1%{?dist} Summary: Single sign-on framework for GNOME License: LGPL-2.0-or-later URL: https://wiki.gnome.org/Projects/GnomeOnlineAccounts -Source0: https://download.gnome.org/sources/gnome-online-accounts/3.47/%{name}-%{version}.tar.xz +Source0: https://download.gnome.org/sources/gnome-online-accounts/3.48/%{name}-%{version}.tar.xz # RHEL >=9 specific # https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/63 # https://bugzilla.redhat.com/show_bug.cgi?id=1913641 Patch0: 0001-google-Remove-Photos-support.patch -#Patch1: kerberos-fixes.patch BuildRequires: pkgconfig(gcr-3) @@ -67,8 +66,6 @@ developing applications that use %{name}. %patch0 -p1 %endif -#%%patch1 -p1 - %build %meson \ -Dexchange=true \ @@ -93,7 +90,7 @@ developing applications that use %{name}. %files -f %{name}.lang %license COPYING -%doc COPYING +%doc NEWS README %dir %{_libdir}/girepository-1.0 %{_libdir}/girepository-1.0/Goa-1.0.typelib %{_libdir}/libgoa-1.0.so.0 @@ -108,8 +105,8 @@ developing applications that use %{name}. %{_datadir}/dbus-1/services/org.gnome.OnlineAccounts.service %{_datadir}/dbus-1/services/org.gnome.Identity.service %{_datadir}/icons/hicolor/*/apps/goa-*.svg -%{_datadir}/man/man8/goa-daemon.8* %{_datadir}/glib-2.0/schemas/org.gnome.online-accounts.gschema.xml +%{_mandir}/man8/goa-daemon.8* %files devel %{_includedir}/goa-1.0/ @@ -126,6 +123,9 @@ developing applications that use %{name}. %{_datadir}/vala/ %changelog +* Sat Mar 18 2023 David King - 3.48.0-1 +- Update to 3.48.0 + * Fri Mar 03 2023 Gwyn Ciesla - 3.47.1-4 - ...but not for flatpaks. diff --git a/kerberos-fixes.patch b/kerberos-fixes.patch deleted file mode 100644 index 8fb0e03..0000000 --- a/kerberos-fixes.patch +++ /dev/null @@ -1,4461 +0,0 @@ -From 8888746f18ad5662a07a48e7e8bf0a4b2d75273d Mon Sep 17 00:00:00 2001 -From: Debarshi Ray -Date: Wed, 12 Oct 2022 22:59:34 +0200 -Subject: [PATCH 01/16] kerberos-identity-manager: Clarify an ambiguous debug - log about KCM - -Kerberos KCM credential caches do support multiple identities and a lot -of users use KCM these days because it's the default on Fedora. Hence, -it's better not to have a debug log that implies that the code wasn't -expecting KCM and is making assumptions about it - KCM is definitely a -supported Kerberos cache type. - -https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/merge_requests/104 ---- - src/goaidentity/goakerberosidentitymanager.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c -index caed5ae2..4fcb132c 100644 ---- a/src/goaidentity/goakerberosidentitymanager.c -+++ b/src/goaidentity/goakerberosidentitymanager.c -@@ -807,61 +807,63 @@ get_identity (GoaKerberosIdentityManager *self, - GoaIdentity *identity; - - g_debug ("GoaKerberosIdentityManager: get identity %s", operation->identifier); - identity = g_hash_table_lookup (self->identities, operation->identifier); - - if (identity == NULL) - { - g_task_return_new_error (operation->task, - GOA_IDENTITY_MANAGER_ERROR, - GOA_IDENTITY_MANAGER_ERROR_IDENTITY_NOT_FOUND, - _("Could not find identity")); - return; - } - - g_task_return_pointer (operation->task, g_object_ref (identity), g_object_unref); - } - - static krb5_error_code - get_new_credentials_cache (GoaKerberosIdentityManager *self, - krb5_ccache *credentials_cache) - { - krb5_error_code error_code; - gboolean supports_multiple_identities; - - if (g_strcmp0 (self->credentials_cache_type, "FILE") == 0) - { - g_debug ("GoaKerberosIdentityManager: credential cache type %s doesn't supports cache collections", - self->credentials_cache_type); - supports_multiple_identities = FALSE; - } -- else if (g_strcmp0 (self->credentials_cache_type, "DIR") == 0 || g_strcmp0 (self->credentials_cache_type, "KEYRING") == 0) -+ else if (g_strcmp0 (self->credentials_cache_type, "DIR") == 0 -+ || g_strcmp0 (self->credentials_cache_type, "KCM") == 0 -+ || g_strcmp0 (self->credentials_cache_type, "KEYRING") == 0) - { - g_debug ("GoaKerberosIdentityManager: credential cache type %s supports cache collections", self->credentials_cache_type); - supports_multiple_identities = TRUE; - } - else - { - g_debug ("GoaKerberosIdentityManager: don't know if credential cache type %s supports cache collections, " - "assuming yes", - self->credentials_cache_type); - supports_multiple_identities = TRUE; - } - - /* If we're configured for FILE based credentials, then we only - * have one ccache, and we need to use it always. - * - * If we're configured for DIR or KEYRING based credentials, then we - * can have multiple ccache's so we should use the default one first - * (so it gets selected automatically) and then fallback to unique - * ccache names for subsequent tickets. - * - */ - if (!supports_multiple_identities || g_hash_table_size (self->identities) == 0) - error_code = krb5_cc_default (self->kerberos_context, credentials_cache); - else - error_code = krb5_cc_new_unique (self->kerberos_context, self->credentials_cache_type, NULL, credentials_cache); - - return error_code; - } - - static void --- -2.39.0 - - -From a25eeeabda86b281665941842d4904b47112f85e Mon Sep 17 00:00:00 2001 -From: Debarshi Ray -Date: Thu, 13 Oct 2022 18:46:46 +0200 -Subject: [PATCH 02/16] kerberos-identity: Clarify and remove redundancy from - the renewal errors - -The "Could not renew identity" segment in the error strings from -goa_kerberos_identity_renew is redundant because the caller already -knows that this function is about renewing an identity and any failure -means just that. In fact, it's callers in GoaIdentityService and -GoaKerberosIdentityManager already prepend a similar segment when using -the error strings. - -Debug logs are already verbose enough. It's better not to make it even -more difficult to follow them through needless redundancy. - -When reporting errors from krb5_cc_get_principal, it's good to make the -error string match the documentation [1] of that function because it -makes the code self-documenting. - -[1] https://web.mit.edu/kerberos/krb5-devel/doc/appdev/refs/api/krb5_cc_get_principal.html - -https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/160 ---- - src/goaidentity/goakerberosidentity.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 45d54f4d..3d2fe25c 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1403,71 +1403,72 @@ goa_kerberos_identity_update (GoaKerberosIdentity *self, - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); - } - else - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - } - queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); - } - } - - gboolean - goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) - { - krb5_error_code error_code = 0; - krb5_principal principal; - krb5_creds new_credentials; - gboolean renewed = FALSE; - char *name = NULL; - - if (self->credentials_cache == NULL) - { - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, -- _("Could not renew identity: Not signed in")); -+ _("Not signed in")); - goto out; - } - - error_code = krb5_cc_get_principal (self->kerberos_context, self->credentials_cache, &principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, -- error_code, _("Could not renew identity: ")); -+ error_code, -+ _("Could not get the default principal: ")); - goto out; - } - - name = goa_kerberos_identity_get_principal_name (self); - - error_code = krb5_get_renewed_creds (self->kerberos_context, &new_credentials, principal, self->credentials_cache, NULL); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_RENEWING, - error_code, - _("Could not get new credentials to renew identity %s: "), - name); - goto free_principal; - } - - if (!goa_kerberos_identity_update_credentials (self, - principal, - &new_credentials, - error)) - { - goto free_principal; - } - - g_debug ("GoaKerberosIdentity: identity %s renewed", name); - renewed = TRUE; - - free_principal: - krb5_free_principal (self->kerberos_context, principal); --- -2.39.0 - - -From 0cd016658de5f30ddf9a8fb749899067d57a3570 Mon Sep 17 00:00:00 2001 -From: Debarshi Ray -Date: Thu, 13 Oct 2022 19:02:59 +0200 -Subject: [PATCH 03/16] kerberos-identity: Clarify the error when talking to - the KDC failed - -When krb5_get_renewed_creds fails to talk to the Kerberos Key -Distribution Centre (or KDC) because of network problems, -krb5_get_error_message translates the krb5_error_code as: - Resource temporarily unavailable - -This ends up in the debug logs as: - GoaKerberosIdentityManager: could not renew identity: Could not get - new credentials to renew identity FOO@BAR.ORG: Resource temporarily - unavailable - GoaIdentityService: could not renew identity: Could not get new - credentials to renew identity FOO@BAR.ORG: Resource temporarily - unavailable - -It's not immediately clear that the 'resource' that's 'temporarily -unavailable' is actually the KDC, which would make it easier to -understand that there is a network problem. - -Therefore, mention KDC in the error string from krb5_get_renewed_creds -and make it match the documentation [1] of that function because it -makes the code self-documenting. - -[1] https://web.mit.edu/kerberos/krb5-devel/doc/appdev/refs/api/krb5_get_renewed_creds.html - -https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/160 ---- - src/goaidentity/goakerberosidentity.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 3d2fe25c..57ab616f 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1427,61 +1427,61 @@ goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) - - if (self->credentials_cache == NULL) - { - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - _("Not signed in")); - goto out; - } - - error_code = krb5_cc_get_principal (self->kerberos_context, self->credentials_cache, &principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - error_code, - _("Could not get the default principal: ")); - goto out; - } - - name = goa_kerberos_identity_get_principal_name (self); - - error_code = krb5_get_renewed_creds (self->kerberos_context, &new_credentials, principal, self->credentials_cache, NULL); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_RENEWING, - error_code, -- _("Could not get new credentials to renew identity %s: "), -+ _("Could not get renewed credentials from the KDC for identity %s: "), - name); - goto free_principal; - } - - if (!goa_kerberos_identity_update_credentials (self, - principal, - &new_credentials, - error)) - { - goto free_principal; - } - - g_debug ("GoaKerberosIdentity: identity %s renewed", name); - renewed = TRUE; - - free_principal: - krb5_free_principal (self->kerberos_context, principal); - - out: - g_free (name); - - return renewed; - } - - gboolean - goa_kerberos_identity_erase (GoaKerberosIdentity *self, GError **error) - { - krb5_error_code error_code = 0; - - if (self->credentials_cache != NULL) --- -2.39.0 - - -From 2e777d41cc6545da48f9e10083f1468887e2f880 Mon Sep 17 00:00:00 2001 -From: Debarshi Ray -Date: Thu, 13 Oct 2022 22:14:07 +0200 -Subject: [PATCH 04/16] kerberos-identity: Fail initialization if an identifier - can't be found - -The inability to get an identifier already leads to an error. -Continuing beyond that point can lead to the verification_error trying -to clobber it. - -https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/merge_requests/107 ---- - src/goaidentity/goakerberosidentity.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 57ab616f..b72ce6ab 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -958,63 +958,64 @@ reset_alarms (GoaKerberosIdentity *self) - g_clear_pointer (&expiration_time, g_date_time_unref); - g_clear_pointer (&latest_possible_renewal_time, g_date_time_unref); - g_clear_pointer (&start_time, g_date_time_unref); - - connect_alarm_signals (self); - } - - static void - clear_alarms (GoaKerberosIdentity *self) - { - disconnect_alarm_signals (self); - clear_alarm_and_unref_on_idle (self, &self->renewal_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiring_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiration_alarm); - } - - static gboolean - goa_kerberos_identity_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable); - GError *verification_error; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - if (self->identifier == NULL) - { - self->identifier = get_identifier (self, error); -+ if (self->identifier == NULL) -+ return FALSE; - -- if (self->identifier != NULL) -- queue_notify (self, &self->identifier_idle_id, "identifier"); -+ queue_notify (self, &self->identifier_idle_id, "identifier"); - } - - verification_error = NULL; - self->cached_verification_level = verify_identity (self, &self->preauth_identity_source, &verification_error); - - switch (self->cached_verification_level) - { - case VERIFICATION_LEVEL_EXISTS: - case VERIFICATION_LEVEL_SIGNED_IN: - reset_alarms (self); - - queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); - return TRUE; - - case VERIFICATION_LEVEL_UNVERIFIED: - return TRUE; - - case VERIFICATION_LEVEL_ERROR: - default: - if (verification_error != NULL) - { - g_propagate_error (error, verification_error); - return FALSE; - } - - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_VERIFYING, - _("No associated identification found")); - return FALSE; --- -2.39.0 - - -From 9f66a5c92a0183a94c28fe24fad615631bc85b1b Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 27 Oct 2022 09:18:53 -0400 -Subject: [PATCH 05/16] kerberos-identity: Ensure idles queued to main thread - are property synchronized - -Kerberos identities are refreshed on a helper thread, and the state of -those identities are exported over the user bus on the main thread. - -Since the main consumer of an identity's properties is the bus service -running on the main thread, to simplify things, property notifications -are dispatched from the main thread as well (even though the underlying -state is changed on a worker thread). - -The mechanism to dispatch property notifies to the main thread is an -idle handler. The logic for doing the dispatch has a concurrency -bug however. In order to coaelsce multiple notifies that happen in -quick succession, the dispatch code checks for a preexisting idle id -associated with the given property. That idle id is set from the worker -thread when the idle is queued, and it's cleared from the main thread -when the idle is dispatched. The bug is that the main thread could in -theory clear the idle id immediately after the worker thread decided -there was already a notify queued, leading to a notify getting -completely dropped. - -This commit addresses the bug by adding appropriate locking. - -Closes https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/160 ---- - src/goaidentity/goakerberosidentity.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index b72ce6ab..695396bf 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -493,61 +493,64 @@ snoop_preauth_identity_from_credentials (GoaKerberosIdentity *self, - static krb5_timestamp - get_current_time (GoaKerberosIdentity *self) - { - krb5_timestamp current_time; - krb5_error_code error_code; - - error_code = krb5_timeofday (self->kerberos_context, ¤t_time); - if (error_code != 0) - { - const char *error_message; - - error_message = krb5_get_error_message (self->kerberos_context, error_code); - g_debug ("GoaKerberosIdentity: Error getting current time: %s", error_message); - krb5_free_error_message (self->kerberos_context, error_message); - return 0; - } - - return current_time; - } - - typedef struct - { - GoaKerberosIdentity *self; - guint *idle_id; - const char *property_name; - } NotifyRequest; - - static void - clear_idle_id (NotifyRequest *request) - { -+ G_LOCK (identity_lock); - *request->idle_id = 0; -+ G_UNLOCK (identity_lock); -+ - g_object_unref (request->self); - g_slice_free (NotifyRequest, request); - } - - static gboolean - on_notify_queued (NotifyRequest *request) - { - g_object_notify (G_OBJECT (request->self), request->property_name); - - return FALSE; - } - - static void - queue_notify (GoaKerberosIdentity *self, - guint *idle_id, - const char *property_name) - { - NotifyRequest *request; - - if (*idle_id != 0) - { - return; - } - - request = g_slice_new0 (NotifyRequest); - request->self = g_object_ref (self); - request->idle_id = idle_id; - request->property_name = property_name; - - *idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, --- -2.39.0 - - -From 6c2b4c40af149f92e446c06af8cb1091370c0504 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 13 Oct 2022 16:11:54 -0400 -Subject: [PATCH 06/16] identity: Don't add temporary accounts for expired - credentials - -The identity service creates a "temporary" kerberos account when -a user manually does kinit, to handle automatic renewal, etc. - -Unfortunately, it also picks up expired cruft that builds up in -KCM based credential caches, and creates temporary accounts for -that as well. - -This commit tries to avoid that. - -Closes https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/32 ---- - src/goaidentity/goaidentityservice.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c -index 3dd27060..a25de416 100644 ---- a/src/goaidentity/goaidentityservice.c -+++ b/src/goaidentity/goaidentityservice.c -@@ -1070,61 +1070,61 @@ add_temporary_account (GoaIdentityService *self, - g_object_ref (identity)); - } - else - { - add_temporary_account_as_kerberos (self, - identity, - NULL, - on_temporary_account_added_as_kerberos, - g_object_ref (identity)); - } - - g_free (realm); - } - - /* ---------------------------------------------------------------------------------------------------- */ - - static void - on_identity_added (GoaIdentityManager *identity_manager, - GoaIdentity *identity, - GoaIdentityService *self) - { - GoaObject *object; - const char *identifier; - - export_identity (self, identity); - - identifier = goa_identity_get_identifier (identity); - - object = find_object_with_principal (self, identifier, FALSE); - -- if (object == NULL) -+ if (object == NULL && goa_identity_is_signed_in (identity)) - add_temporary_account (self, identity); - - g_clear_object (&object); - } - - static void - on_identity_removed (GoaIdentityManager *identity_manager, - GoaIdentity *identity, - GoaIdentityService *self) - { - GoaObject *object; - const char *identifier; - - identifier = goa_identity_get_identifier (identity); - object = find_object_with_principal (self, identifier, FALSE); - - if (object != NULL) - ensure_account_credentials (self, object); - - unexport_identity (self, identity); - g_clear_object (&object); - } - - static void - on_identity_refreshed (GoaIdentityManager *identity_manager, - GoaIdentity *identity, - GoaIdentityService *self) - { - GoaObject *object; - const char *identifier; --- -2.39.0 - - -From aca400799c225a84e5d0fc90efb206c8f1d48bc3 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Mon, 28 Nov 2022 14:16:09 -0500 -Subject: [PATCH 07/16] kerberos-identity: Attempt to cope with multiple - credential caches per identity - -At the moment the identity service assumes there will just be one -credential cache collection for any given prinicipal. - -This isn't necessarily true though, and the service gets quite -confused when that assumption doesn't hold up. - -This commit attempts to make it cope with the situation better, by -maintaining a hash table of collections per identity. It deems -one of the collections the "active" one and relegates the rest to -be backup if the active one expires and can't be renewed. - -Closes: https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/issues/79 ---- - src/goaidentity/goakerberosidentity.c | 340 ++++++++++++++++++++++---- - 1 file changed, 287 insertions(+), 53 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 695396bf..dbb5991d 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -6,73 +6,76 @@ - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, see . - */ - - #include "config.h" - - #include "goaidentity.h" - #include "goakerberosidentity.h" - #include "goakerberosidentityinquiry.h" - #include "goaalarm.h" - - #include - #include - #include - - #include - #include - #include - - typedef enum - { -+ VERIFICATION_LEVEL_ERROR = -1, - VERIFICATION_LEVEL_UNVERIFIED, -- VERIFICATION_LEVEL_ERROR, - VERIFICATION_LEVEL_EXISTS, - VERIFICATION_LEVEL_SIGNED_IN - } VerificationLevel; - - struct _GoaKerberosIdentity - { - GObject parent; - - krb5_context kerberos_context; - krb5_ccache credentials_cache; - -+ GHashTable *credentials_caches; -+ char *active_credentials_cache_name; -+ - char *identifier; - guint identifier_idle_id; - - char *preauth_identity_source; - - krb5_timestamp start_time; - guint start_time_idle_id; - krb5_timestamp renewal_time; - guint renewal_time_idle_id; - krb5_timestamp expiration_time; - guint expiration_time_idle_id; - - GoaAlarm *expiration_alarm; - GoaAlarm *expiring_alarm; - GoaAlarm *renewal_alarm; - - VerificationLevel cached_verification_level; - guint is_signed_in_idle_id; - }; - - enum - { - EXPIRING, - EXPIRED, - UNEXPIRED, - NEEDS_RENEWAL, - NEEDS_REFRESH, - NUMBER_OF_SIGNALS, - }; - -@@ -82,84 +85,99 @@ enum - PROP_IDENTIFIER, - PROP_IS_SIGNED_IN, - PROP_START_TIMESTAMP, - PROP_RENEWAL_TIMESTAMP, - PROP_EXPIRATION_TIMESTAMP - }; - - static guint signals[NUMBER_OF_SIGNALS] = { 0 }; - - static void identity_interface_init (GoaIdentityInterface *interface); - static void initable_interface_init (GInitableIface *interface); - static void reset_alarms (GoaKerberosIdentity *self); - static void clear_alarms (GoaKerberosIdentity *self); - static gboolean goa_kerberos_identity_is_signed_in (GoaIdentity *identity); - static void set_and_prefix_error_from_krb5_error_code (GoaKerberosIdentity *self, - GError **error, - gint code, - krb5_error_code error_code, - const char *format, - ...) G_GNUC_PRINTF (5, 6); - - G_LOCK_DEFINE_STATIC (identity_lock); - - G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentity, - goa_kerberos_identity, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - initable_interface_init) - G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY, - identity_interface_init)); -+ -+static void -+close_credentials_caches (GoaKerberosIdentity *self) -+{ -+ GHashTableIter iter; -+ const char *name; -+ krb5_ccache credentials_cache; -+ -+ g_hash_table_iter_init (&iter, self->credentials_caches); -+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) -+ { -+ krb5_cc_close (self->kerberos_context, credentials_cache); -+ } -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+} -+ - static void - goa_kerberos_identity_dispose (GObject *object) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object); - - G_LOCK (identity_lock); - clear_alarms (self); - g_clear_pointer (&self->preauth_identity_source, g_free); -+ close_credentials_caches (self); -+ g_clear_pointer (&self->credentials_caches, g_hash_table_unref); - G_UNLOCK (identity_lock); - - G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->dispose (object); - - } - - static void - goa_kerberos_identity_finalize (GObject *object) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object); - - g_free (self->identifier); - -- if (self->credentials_cache != NULL) -- krb5_cc_close (self->kerberos_context, self->credentials_cache); -- - G_OBJECT_CLASS (goa_kerberos_identity_parent_class)->finalize (object); - } - - static void - goa_kerberos_identity_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *param_spec) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object); - - switch (property_id) - { - case PROP_IDENTIFIER: - G_LOCK (identity_lock); - g_value_set_string (value, self->identifier); - G_UNLOCK (identity_lock); - break; - case PROP_IS_SIGNED_IN: - g_value_set_boolean (value, - goa_kerberos_identity_is_signed_in (GOA_IDENTITY (self))); - break; - case PROP_START_TIMESTAMP: - G_LOCK (identity_lock); - g_value_set_int64 (value, (gint64) self->start_time); - G_UNLOCK (identity_lock); - break; - case PROP_RENEWAL_TIMESTAMP: - G_LOCK (identity_lock); - g_value_set_int64 (value, (gint64) self->renewal_time); -@@ -228,109 +246,114 @@ goa_kerberos_identity_class_init (GoaKerberosIdentityClass *klass) - G_SIGNAL_RUN_LAST, - 0, - NULL, - NULL, - NULL, - G_TYPE_NONE, - 0); - - g_object_class_override_property (object_class, PROP_IDENTIFIER, "identifier"); - g_object_class_override_property (object_class, PROP_IS_SIGNED_IN, "is-signed-in"); - g_object_class_override_property (object_class, - PROP_START_TIMESTAMP, - "start-timestamp"); - g_object_class_override_property (object_class, - PROP_RENEWAL_TIMESTAMP, - "renewal-timestamp"); - g_object_class_override_property (object_class, - PROP_EXPIRATION_TIMESTAMP, - "expiration-timestamp"); - - } - - static char * - get_identifier (GoaKerberosIdentity *self, - GError **error) - { - krb5_principal principal; - krb5_error_code error_code; - char *unparsed_name; - char *identifier = NULL; -+ krb5_ccache credentials_cache; - -- if (self->credentials_cache == NULL) -+ if (self->active_credentials_cache_name == NULL) - return NULL; - -- error_code = krb5_cc_get_principal (self->kerberos_context, self->credentials_cache, &principal); -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ -+ error_code = krb5_cc_get_principal (self->kerberos_context, credentials_cache, &principal); - if (error_code != 0) - { - if (error_code == KRB5_CC_END) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - error_code, - _("Could not find identity in credential cache: ")); - } - else - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, - error_code, - _("Could not find identity in credential cache: ")); - } - return NULL; - } - - error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &unparsed_name); - if (error_code != 0) - { - const char *error_message; - - error_message = krb5_get_error_message (self->kerberos_context, error_code); - g_debug ("GoaKerberosIdentity: Error parsing principal identity name: %s", - error_message); - krb5_free_error_message (self->kerberos_context, error_message); - goto out; - } - - identifier = g_strdup (unparsed_name); - krb5_free_unparsed_name (self->kerberos_context, unparsed_name); - - out: - krb5_free_principal (self->kerberos_context, principal); - return identifier; - } - - static void - goa_kerberos_identity_init (GoaKerberosIdentity *self) - { -+ self->credentials_caches = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - } - - static void - set_and_prefix_error_from_krb5_error_code (GoaKerberosIdentity *self, - GError **error, - gint code, - krb5_error_code error_code, - const char *format, - ...) - { - const char *error_message; - char *literal_prefix; - va_list args; - - error_message = krb5_get_error_message (self->kerberos_context, error_code); - g_set_error_literal (error, GOA_IDENTITY_ERROR, code, error_message); - - va_start (args, format); - literal_prefix = g_strdup_vprintf (format, args); - va_end (args); - - g_prefix_error (error, "%s", literal_prefix); - - g_free (literal_prefix); - krb5_free_error_message (self->kerberos_context, error_message); - } - - char * - goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self) - { -@@ -613,169 +636,280 @@ examine_credentials (GoaKerberosIdentity *self, - krb5_timestamp current_time; - - G_LOCK (identity_lock); - - if (credentials->times.starttime != 0) - credentials_start_time = credentials->times.starttime; - else - credentials_start_time = credentials->times.authtime; - - *renewal_time = credentials->times.renew_till; - - credentials_end_time = credentials->times.endtime; - - if (self->start_time == 0) - *start_time = credentials_start_time; - else - *start_time = MIN (self->start_time, credentials_start_time); - *expiration_time = MAX (credentials->times.endtime, self->expiration_time); - G_UNLOCK (identity_lock); - - current_time = get_current_time (self); - - if (current_time < credentials_start_time || - credentials_end_time <= current_time) - *are_expired = TRUE; - else - *are_expired = FALSE; - } - - static VerificationLevel --verify_identity (GoaKerberosIdentity *self, -- char **preauth_identity_source, -- GError **error) -+verify_identity_in_credentials_cache (GoaKerberosIdentity *self, -+ char **preauth_identity_source, -+ krb5_ccache credentials_cache, -+ krb5_timestamp *start_time, -+ krb5_timestamp *renewal_time, -+ krb5_timestamp *expiration_time, -+ GError **error) - { - krb5_principal principal = NULL; - krb5_cc_cursor cursor; - krb5_creds credentials; - krb5_error_code error_code; -- krb5_timestamp start_time = 0; -- krb5_timestamp renewal_time = 0; -- krb5_timestamp expiration_time = 0; - VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED; - -- if (self->credentials_cache == NULL) -- goto out; -+ g_debug ("GoaKerberosIdentity: Verifying identity in credentials cache '%s'", -+ krb5_cc_get_name (self->kerberos_context, credentials_cache)); - -- error_code = krb5_cc_get_principal (self->kerberos_context, self->credentials_cache, &principal); -+ error_code = krb5_cc_get_principal (self->kerberos_context, credentials_cache, &principal); - if (error_code != 0) - { -+ if (error_code == KRB5_CC_END) -+ g_debug ("GoaKerberosIdentity: Credentials cache empty"); -+ else if (error_code == KRB5_FCC_NOFILE) -+ g_debug ("GoaKerberosIdentity: Credentials cache missing"); -+ - if (error_code == KRB5_CC_END || error_code == KRB5_FCC_NOFILE) - goto out; - - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_NOT_FOUND, - error_code, - _("Could not find identity in credential cache: ")); - verification_level = VERIFICATION_LEVEL_ERROR; - goto out; - } - -- error_code = krb5_cc_start_seq_get (self->kerberos_context, self->credentials_cache, &cursor); -+ error_code = krb5_cc_start_seq_get (self->kerberos_context, credentials_cache, &cursor); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - error_code, - _("Could not find identity credentials in cache: ")); - - verification_level = VERIFICATION_LEVEL_ERROR; - goto out; - } - - verification_level = VERIFICATION_LEVEL_UNVERIFIED; - -- error_code = krb5_cc_next_cred (self->kerberos_context, self->credentials_cache, &cursor, &credentials); -+ error_code = krb5_cc_next_cred (self->kerberos_context, credentials_cache, &cursor, &credentials); - while (error_code == 0) - { - if (credentials_validate_existence (self, principal, &credentials)) - { - gboolean credentials_are_expired = TRUE; - -- examine_credentials (self, &credentials, -- &start_time, -- &renewal_time, -- &expiration_time, -+ examine_credentials (self, -+ &credentials, -+ start_time, -+ renewal_time, -+ expiration_time, - &credentials_are_expired); - - if (!credentials_are_expired) - verification_level = VERIFICATION_LEVEL_SIGNED_IN; - else - verification_level = VERIFICATION_LEVEL_EXISTS; - } - else - { - snoop_preauth_identity_from_credentials (self, &credentials, preauth_identity_source); - } - - krb5_free_cred_contents (self->kerberos_context, &credentials); -- error_code = krb5_cc_next_cred (self->kerberos_context, self->credentials_cache, &cursor, &credentials); -+ error_code = krb5_cc_next_cred (self->kerberos_context, credentials_cache, &cursor, &credentials); - } - - if (error_code != KRB5_CC_END) - { - verification_level = VERIFICATION_LEVEL_ERROR; - - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, - error_code, - _("Could not sift through identity credentials in cache: ")); -- goto end_sequence; - } - -- end_sequence: -- error_code = krb5_cc_end_seq_get (self->kerberos_context, self->credentials_cache, &cursor); -+ error_code = krb5_cc_end_seq_get (self->kerberos_context, credentials_cache, &cursor); - if (error_code != 0) - { - verification_level = VERIFICATION_LEVEL_ERROR; - - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, - error_code, - _("Could not finish up sifting through " - "identity credentials in cache: ")); - goto out; - } -+ - out: -+ switch (verification_level) -+ { -+ case VERIFICATION_LEVEL_EXISTS: -+ g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are out of date", -+ krb5_cc_get_name (self->kerberos_context, credentials_cache)); -+ break; -+ -+ case VERIFICATION_LEVEL_SIGNED_IN: -+ g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are valid", -+ krb5_cc_get_name (self->kerberos_context, credentials_cache)); -+ break; -+ -+ case VERIFICATION_LEVEL_UNVERIFIED: -+ g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are missing", -+ krb5_cc_get_name (self->kerberos_context, credentials_cache)); -+ break; -+ -+ case VERIFICATION_LEVEL_ERROR: -+ default: -+ g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' could not be validated", -+ krb5_cc_get_name (self->kerberos_context, credentials_cache)); -+ break; -+ } -+ -+ if (principal != NULL) -+ krb5_free_principal (self->kerberos_context, principal); -+ return verification_level; -+} -+ -+static VerificationLevel -+verify_identity (GoaKerberosIdentity *self, -+ char **preauth_identity_source, -+ GError **error) -+{ -+ krb5_ccache credentials_cache; -+ const char *name; -+ krb5_timestamp start_time = 0; -+ krb5_timestamp renewal_time = 0; -+ krb5_timestamp expiration_time = 0; -+ VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED; -+ GHashTableIter iter; -+ -+ if (self->active_credentials_cache_name != NULL) -+ { -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ -+ verification_level = verify_identity_in_credentials_cache (self, -+ preauth_identity_source, -+ credentials_cache, -+ &start_time, -+ &renewal_time, -+ &expiration_time, -+ error); -+ if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) -+ goto out; -+ -+ if (verification_level == VERIFICATION_LEVEL_UNVERIFIED) -+ { -+ krb5_cc_close (self->kerberos_context, credentials_cache); -+ g_hash_table_remove (self->credentials_caches, self->active_credentials_cache_name); -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ } -+ } -+ -+ self->start_time = 0; -+ self->renewal_time = 0; -+ self->expiration_time = 0; -+ -+ g_hash_table_iter_init (&iter, self->credentials_caches); -+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) -+ { -+ krb5_timestamp new_start_time = 0; -+ krb5_timestamp new_renewal_time = 0; -+ krb5_timestamp new_expiration_time = 0; -+ -+ if (g_strcmp0 (name, self->active_credentials_cache_name) == 0) -+ continue; -+ -+ g_clear_pointer (preauth_identity_source, g_free); -+ verification_level = verify_identity_in_credentials_cache (self, -+ preauth_identity_source, -+ credentials_cache, -+ &new_start_time, -+ &new_renewal_time, -+ &new_expiration_time, -+ error); -+ -+ if (verification_level == VERIFICATION_LEVEL_SIGNED_IN || -+ self->active_credentials_cache_name == NULL) -+ { -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ self->active_credentials_cache_name = g_strdup (name); -+ start_time = new_start_time; -+ renewal_time = new_renewal_time; -+ expiration_time = new_expiration_time; -+ -+ if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) -+ break; -+ } -+ else if (verification_level == VERIFICATION_LEVEL_UNVERIFIED) -+ { -+ krb5_cc_close (self->kerberos_context, credentials_cache); -+ g_hash_table_iter_remove (&iter); -+ } -+ } - -+out: - G_LOCK (identity_lock); - set_start_time (self, start_time); - set_renewal_time (self, renewal_time); - set_expiration_time (self, expiration_time); - G_UNLOCK (identity_lock); - -- if (principal != NULL) -- krb5_free_principal (self->kerberos_context, principal); - return verification_level; - } - - static gboolean - goa_kerberos_identity_is_signed_in (GoaIdentity *identity) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (identity); - gboolean is_signed_in = FALSE; - - G_LOCK (identity_lock); - if (self->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - is_signed_in = TRUE; - G_UNLOCK (identity_lock); - - return is_signed_in; - } - - static void - identity_interface_init (GoaIdentityInterface *interface) - { - interface->get_identifier = goa_kerberos_identity_get_identifier; - interface->is_signed_in = goa_kerberos_identity_is_signed_in; - } - - static void - on_expiration_alarm_fired (GoaAlarm *alarm, - GoaKerberosIdentity *self) - { - g_return_if_fail (GOA_IS_ALARM (alarm)); - g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); -@@ -1052,121 +1186,154 @@ on_kerberos_inquiry (krb5_context kerberos_context, - GoaIdentityInquiry *inquiry; - krb5_error_code error_code = 0; - - if (number_of_prompts == 0) - goto out; - - inquiry = goa_kerberos_identity_inquiry_new (operation->identity, - name, - banner, - prompts, - number_of_prompts); - - operation->inquiry_func (inquiry, - operation->cancellable, - operation->inquiry_data); - - if (goa_identity_inquiry_is_failed (inquiry)) - error_code = KRB5_LIBOS_CANTREADPWD; - else if (!goa_identity_inquiry_is_complete (inquiry)) - g_cancellable_cancel (operation->cancellable); - - if (g_cancellable_is_cancelled (operation->cancellable)) - error_code = KRB5_LIBOS_PWDINTR; - - g_object_unref (inquiry); - - out: - return error_code; - } - -+static void -+goa_kerberos_identity_add_credentials_cache (GoaKerberosIdentity *self, -+ krb5_ccache credentials_cache) -+{ -+ const char *cache_name; -+ -+ cache_name = krb5_cc_get_name (self->kerberos_context, credentials_cache); -+ -+ if (g_hash_table_contains (self->credentials_caches, cache_name)) -+ { -+ krb5_ccache old_credentials_cache; -+ -+ old_credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, cache_name); -+ -+ krb5_cc_close (self->kerberos_context, old_credentials_cache); -+ } -+ -+ g_hash_table_replace (self->credentials_caches, g_strdup (cache_name), credentials_cache); -+ -+ if (self->active_credentials_cache_name == NULL) -+ { -+ self->active_credentials_cache_name = g_strdup (cache_name); -+ } -+} -+ - static gboolean --create_credential_cache (GoaKerberosIdentity *self, -- GError **error) -+create_credentials_cache (GoaKerberosIdentity *self, -+ GError **error) - { - krb5_ccache default_cache; -+ krb5_ccache new_cache; - const char *cache_type; - krb5_error_code error_code; - - error_code = krb5_cc_default (self->kerberos_context, &default_cache); - - if (error_code == 0) - { - cache_type = krb5_cc_get_type (self->kerberos_context, default_cache); -- error_code = krb5_cc_new_unique (self->kerberos_context, cache_type, NULL, &self->credentials_cache); -+ error_code = krb5_cc_new_unique (self->kerberos_context, cache_type, NULL, &new_cache); - } - - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS, - error_code, - _("Could not create credential cache: ")); - - return FALSE; - } - -+ goa_kerberos_identity_add_credentials_cache (self, new_cache); -+ - return TRUE; - } - - static gboolean - goa_kerberos_identity_update_credentials (GoaKerberosIdentity *self, - krb5_principal principal, - krb5_creds *new_credentials, - GError **error) - { - krb5_error_code error_code; -+ krb5_ccache credentials_cache; - -- if (self->credentials_cache == NULL) -+ -+ if (self->active_credentials_cache_name == NULL) - { -- if (!create_credential_cache (self, error)) -+ if (!create_credentials_cache (self, error)) - { - krb5_free_cred_contents (self->kerberos_context, new_credentials); - goto out; - } - } - -- error_code = krb5_cc_initialize (self->kerberos_context, self->credentials_cache, principal); -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ -+ error_code = krb5_cc_initialize (self->kerberos_context, credentials_cache, principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS, - error_code, - _("Could not initialize credentials cache: ")); - - krb5_free_cred_contents (self->kerberos_context, new_credentials); - goto out; - } - -- error_code = krb5_cc_store_cred (self->kerberos_context, self->credentials_cache, new_credentials); -+ error_code = krb5_cc_store_cred (self->kerberos_context, credentials_cache, new_credentials); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_SAVING_CREDENTIALS, - error_code, - _("Could not store new credentials in credentials cache: ")); - - krb5_free_cred_contents (self->kerberos_context, new_credentials); - goto out; - } - krb5_free_cred_contents (self->kerberos_context, new_credentials); - - return TRUE; - out: - return FALSE; - } - - static SignInOperation * - sign_in_operation_new (GoaKerberosIdentity *identity, - GoaIdentityInquiryFunc inquiry_func, - gpointer inquiry_data, - GDestroyNotify destroy_notify, - GCancellable *cancellable) - { - SignInOperation *operation; - - operation = g_slice_new0 (SignInOperation); - operation->identity = g_object_ref (identity); - operation->inquiry_func = inquiry_func; -@@ -1327,201 +1494,268 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, - krb5_free_principal (self->kerberos_context, principal); - goto done; - } - krb5_free_principal (self->kerberos_context, principal); - - g_debug ("GoaKerberosIdentity: identity signed in"); - signed_in = TRUE; - done: - - return signed_in; - } - - static void - update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity) - { - char *new_identifier; - - new_identifier = get_identifier (self, NULL); - if (g_strcmp0 (self->identifier, new_identifier) != 0 && new_identifier != NULL) - { - g_free (self->identifier); - self->identifier = new_identifier; - queue_notify (self, &self->identifier_idle_id, "identifier"); - } - else - { - g_free (new_identifier); - } - } - -+static int -+goa_kerberos_identity_compare (GoaKerberosIdentity *self, -+ GoaKerberosIdentity *new_identity) -+{ -+ if (self->cached_verification_level < new_identity->cached_verification_level) -+ return -100; -+ -+ if (self->cached_verification_level > new_identity->cached_verification_level) -+ return 100; -+ -+ if (self->cached_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -+ return 50; -+ -+ if (self->expiration_time < new_identity->expiration_time) -+ return -10; -+ -+ if (self->expiration_time > new_identity->expiration_time) -+ return 10; -+ -+ if (self->start_time > new_identity->start_time) -+ return -5; -+ -+ if (self->start_time < new_identity->start_time) -+ return 5; -+ -+ if (self->renewal_time < new_identity->renewal_time) -+ return -1; -+ -+ if (self->renewal_time > new_identity->renewal_time) -+ return 1; -+ -+ return 0; -+} -+ - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; -+ int comparison; -+ -+ comparison = goa_kerberos_identity_compare (self, new_identity); -+ -+ if (new_identity->active_credentials_cache_name != NULL) -+ { -+ krb5_ccache credentials_cache; -+ krb5_ccache copied_cache; - -- if (self->credentials_cache != NULL) -- krb5_cc_close (self->kerberos_context, self->credentials_cache); -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, -+ new_identity->active_credentials_cache_name); -+ krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - -- krb5_cc_dup (new_identity->kerberos_context, new_identity->credentials_cache, &self->credentials_cache); -+ if (comparison < 0) -+ g_clear_pointer (&self->active_credentials_cache_name, &g_free); -+ -+ goa_kerberos_identity_add_credentials_cache (self, copied_cache); -+ } -+ -+ if (comparison >= 0) -+ return; - - G_LOCK (identity_lock); - update_identifier (self, new_identity); -- - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); - old_verification_level = self->cached_verification_level; - new_verification_level = new_identity->cached_verification_level; - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - reset_alarms (self); - else - clear_alarms (self); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { - if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && - new_verification_level == VERIFICATION_LEVEL_EXISTS) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); - } - else if (old_verification_level == VERIFICATION_LEVEL_EXISTS && - new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); - } - else - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - } - queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); - } - } - - gboolean - goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) - { - krb5_error_code error_code = 0; - krb5_principal principal; - krb5_creds new_credentials; -+ krb5_ccache credentials_cache; - gboolean renewed = FALSE; - char *name = NULL; - -- if (self->credentials_cache == NULL) -+ if (self->active_credentials_cache_name == NULL) - { - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - _("Not signed in")); - goto out; - } - -- error_code = krb5_cc_get_principal (self->kerberos_context, self->credentials_cache, &principal); -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ error_code = krb5_cc_get_principal (self->kerberos_context, credentials_cache, &principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - error_code, - _("Could not get the default principal: ")); - goto out; - } - - name = goa_kerberos_identity_get_principal_name (self); - -- error_code = krb5_get_renewed_creds (self->kerberos_context, &new_credentials, principal, self->credentials_cache, NULL); -+ error_code = krb5_get_renewed_creds (self->kerberos_context, &new_credentials, principal, credentials_cache, NULL); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_RENEWING, - error_code, - _("Could not get renewed credentials from the KDC for identity %s: "), - name); - goto free_principal; - } - - if (!goa_kerberos_identity_update_credentials (self, - principal, - &new_credentials, - error)) - { - goto free_principal; - } - - g_debug ("GoaKerberosIdentity: identity %s renewed", name); - renewed = TRUE; - - free_principal: - krb5_free_principal (self->kerberos_context, principal); - - out: - g_free (name); - - return renewed; - } - - gboolean - goa_kerberos_identity_erase (GoaKerberosIdentity *self, GError **error) - { -+ GHashTableIter iter; -+ const char *name; -+ krb5_ccache credentials_cache; - krb5_error_code error_code = 0; - -- if (self->credentials_cache != NULL) -+ if (self->active_credentials_cache_name != NULL) - { -- error_code = krb5_cc_destroy (self->kerberos_context, self->credentials_cache); -- self->credentials_cache = NULL; -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ g_debug ("GoaKerberosIdentity: Destroying active credentials cache %s", self->active_credentials_cache_name); -+ error_code = krb5_cc_destroy (self->kerberos_context, credentials_cache); -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ -+ if (error_code != 0) -+ { -+ set_and_prefix_error_from_krb5_error_code (self, -+ error, -+ GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, -+ error_code, _("Could not erase identity: ")); -+ } - } - -- if (error_code != 0) -+ g_hash_table_iter_init (&iter, self->credentials_caches); -+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) - { -- set_and_prefix_error_from_krb5_error_code (self, -- error, -- GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, -- error_code, _("Could not erase identity: ")); -- return FALSE; -+ g_debug ("GoaKerberosIdentity: Destroying inactive credentials cache %s", name); -+ krb5_cc_destroy (self->kerberos_context, credentials_cache); - } -+ g_hash_table_remove_all (self->credentials_caches); - -- return TRUE; -+ return error_code == 0; - } - - GoaIdentity * - goa_kerberos_identity_new (krb5_context context, krb5_ccache cache, GError **error) - { - GoaKerberosIdentity *self; -+ krb5_ccache copied_cache; - - self = GOA_KERBEROS_IDENTITY (g_object_new (GOA_TYPE_KERBEROS_IDENTITY, NULL)); -- -- krb5_cc_dup (context, cache, &self->credentials_cache); - self->kerberos_context = context; - -+ krb5_cc_dup (self->kerberos_context, cache, &copied_cache); -+ goa_kerberos_identity_add_credentials_cache (self, copied_cache); -+ - error = NULL; - if (!g_initable_init (G_INITABLE (self), NULL, error)) - { - g_object_unref (self); - return NULL; - } - - return GOA_IDENTITY (self); - } --- -2.39.0 - - -From 5f61f503de118c716809a18484e60a6d5a55b6e3 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Mon, 28 Nov 2022 15:58:09 -0500 -Subject: [PATCH 08/16] kerberos-identity: Clear alarms on temporary identity - -When the identity service does a refresh, it creates a new temporary -identity object to check the credentials, then it merges that -temporary identity into the preexisting identity object (so the -pointers don't change). - -This has the unfortunate side-effect of arming expiration alarms in -the temporary object, that can then fire immediately before the object -is thrown out. - -This commit disarms those alarms so they don't fire needlessly. ---- - src/goaidentity/goakerberosidentity.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index dbb5991d..6006385b 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1554,60 +1554,62 @@ goa_kerberos_identity_compare (GoaKerberosIdentity *self, - - return 0; - } - - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; - int comparison; - - comparison = goa_kerberos_identity_compare (self, new_identity); - - if (new_identity->active_credentials_cache_name != NULL) - { - krb5_ccache credentials_cache; - krb5_ccache copied_cache; - - credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, - new_identity->active_credentials_cache_name); - krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - - if (comparison < 0) - g_clear_pointer (&self->active_credentials_cache_name, &g_free); - - goa_kerberos_identity_add_credentials_cache (self, copied_cache); - } - -+ clear_alarms (new_identity); -+ - if (comparison >= 0) - return; - - G_LOCK (identity_lock); - update_identifier (self, new_identity); - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); - old_verification_level = self->cached_verification_level; - new_verification_level = new_identity->cached_verification_level; - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - reset_alarms (self); - else - clear_alarms (self); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { - if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && - new_verification_level == VERIFICATION_LEVEL_EXISTS) - { --- -2.39.0 - - -From e3821a2746b3a1f01dedece09f33f8b4d7573713 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Tue, 29 Nov 2022 12:21:09 -0500 -Subject: [PATCH 09/16] kerberos-identity: Add missing locking - -commit c492cbfd861bc773cf8b4c15bc722380355fc4b3 introduced some -code to goa_kerberos_identity_update that's not protected by the -identity lock. - -It really should be. - -This commit fixes that. ---- - src/goaidentity/goakerberosidentity.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 6006385b..b51c9920 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1537,76 +1537,78 @@ goa_kerberos_identity_compare (GoaKerberosIdentity *self, - if (self->expiration_time < new_identity->expiration_time) - return -10; - - if (self->expiration_time > new_identity->expiration_time) - return 10; - - if (self->start_time > new_identity->start_time) - return -5; - - if (self->start_time < new_identity->start_time) - return 5; - - if (self->renewal_time < new_identity->renewal_time) - return -1; - - if (self->renewal_time > new_identity->renewal_time) - return 1; - - return 0; - } - - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; - int comparison; - -+ G_LOCK (identity_lock); - comparison = goa_kerberos_identity_compare (self, new_identity); - - if (new_identity->active_credentials_cache_name != NULL) - { - krb5_ccache credentials_cache; - krb5_ccache copied_cache; - - credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, - new_identity->active_credentials_cache_name); - krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - - if (comparison < 0) - g_clear_pointer (&self->active_credentials_cache_name, &g_free); - - goa_kerberos_identity_add_credentials_cache (self, copied_cache); - } -+ G_UNLOCK (identity_lock); - - clear_alarms (new_identity); - - if (comparison >= 0) - return; - - G_LOCK (identity_lock); - update_identifier (self, new_identity); - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); - old_verification_level = self->cached_verification_level; - new_verification_level = new_identity->cached_verification_level; - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - reset_alarms (self); - else - clear_alarms (self); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { --- -2.39.0 - - -From 9eaf73c81528aae5c89ac9b3366f48d30bca4794 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Wed, 30 Nov 2022 13:53:41 -0500 -Subject: [PATCH 10/16] kerberos-identity: Drop the weird ampersand - -This commit drops an unnecessary and non-idiomatic ampersand. ---- - src/goaidentity/goakerberosidentity.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index b51c9920..55288d24 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1550,61 +1550,61 @@ goa_kerberos_identity_compare (GoaKerberosIdentity *self, - return -1; - - if (self->renewal_time > new_identity->renewal_time) - return 1; - - return 0; - } - - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; - int comparison; - - G_LOCK (identity_lock); - comparison = goa_kerberos_identity_compare (self, new_identity); - - if (new_identity->active_credentials_cache_name != NULL) - { - krb5_ccache credentials_cache; - krb5_ccache copied_cache; - - credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, - new_identity->active_credentials_cache_name); - krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - - if (comparison < 0) -- g_clear_pointer (&self->active_credentials_cache_name, &g_free); -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); - - goa_kerberos_identity_add_credentials_cache (self, copied_cache); - } - G_UNLOCK (identity_lock); - - clear_alarms (new_identity); - - if (comparison >= 0) - return; - - G_LOCK (identity_lock); - update_identifier (self, new_identity); - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); - old_verification_level = self->cached_verification_level; - new_verification_level = new_identity->cached_verification_level; - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - reset_alarms (self); - else - clear_alarms (self); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; --- -2.39.0 - - -From 54f40b8c5696cd15e371f428eccc9090c37ebe65 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 15 Dec 2022 14:46:01 -0500 -Subject: [PATCH 11/16] kerberos-identity: Unbreak handling of fresh caches - -commit 4acfcc323e986526975ede981673dd173be4e267 attempted to -avoid an error variable getting stomped all over by returning -FALSE when encountering the error. - -Unfortunately, it's actual legitimate for an error to happen -in that path and we should proceed anyway. - -That can happen when a credential cache is new and not yet -initialized, so it won't have a principal associated with it -yet. - -This commit changes the problematic code to just pass NULL -for the error variable, since we don't need it. ---- - src/goaidentity/goakerberosidentity.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 55288d24..a20c0438 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1094,65 +1094,64 @@ reset_alarms (GoaKerberosIdentity *self) - g_clear_pointer (&renewal_time, g_date_time_unref); - g_clear_pointer (&expiration_time, g_date_time_unref); - g_clear_pointer (&latest_possible_renewal_time, g_date_time_unref); - g_clear_pointer (&start_time, g_date_time_unref); - - connect_alarm_signals (self); - } - - static void - clear_alarms (GoaKerberosIdentity *self) - { - disconnect_alarm_signals (self); - clear_alarm_and_unref_on_idle (self, &self->renewal_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiring_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiration_alarm); - } - - static gboolean - goa_kerberos_identity_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable); - GError *verification_error; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - if (self->identifier == NULL) - { -- self->identifier = get_identifier (self, error); -- if (self->identifier == NULL) -- return FALSE; -+ self->identifier = get_identifier (self, NULL); - -- queue_notify (self, &self->identifier_idle_id, "identifier"); -+ if (self->identifier != NULL) -+ queue_notify (self, &self->identifier_idle_id, "identifier"); - } - - verification_error = NULL; - self->cached_verification_level = verify_identity (self, &self->preauth_identity_source, &verification_error); - - switch (self->cached_verification_level) - { - case VERIFICATION_LEVEL_EXISTS: - case VERIFICATION_LEVEL_SIGNED_IN: - reset_alarms (self); - - queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); - return TRUE; - - case VERIFICATION_LEVEL_UNVERIFIED: - return TRUE; - - case VERIFICATION_LEVEL_ERROR: - default: - if (verification_error != NULL) - { - g_propagate_error (error, verification_error); - return FALSE; - } - - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_VERIFYING, - _("No associated identification found")); - return FALSE; --- -2.39.0 - - -From 09856fdebf7c6dc70a6d07fd76197eb42fdb9262 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 15 Dec 2022 14:46:01 -0500 -Subject: [PATCH 12/16] kerberos-identity: Fix buglet in update_identity - -The update_identity function is supposed to transfer the identity -form one object to another. - -In practice, this is currently always a noop because only objects -with the same identities get copied to each other. - -Nevertheless, there is a bug in the function. It grabs the identity -from the target object instead of from the source object. - -This commit fixes that. ---- - src/goaidentity/goakerberosidentity.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index a20c0438..bc607966 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1480,61 +1480,61 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, - krb5_free_principal (self->kerberos_context, principal); - goto done; - } - - if (destroy_notify) - destroy_notify (inquiry_data); - sign_in_operation_free (operation); - - if (!goa_kerberos_identity_update_credentials (self, - principal, - &new_credentials, - error)) - { - krb5_free_principal (self->kerberos_context, principal); - goto done; - } - krb5_free_principal (self->kerberos_context, principal); - - g_debug ("GoaKerberosIdentity: identity signed in"); - signed_in = TRUE; - done: - - return signed_in; - } - - static void - update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity) - { - char *new_identifier; - -- new_identifier = get_identifier (self, NULL); -+ new_identifier = get_identifier (new_identity, NULL); - if (g_strcmp0 (self->identifier, new_identifier) != 0 && new_identifier != NULL) - { - g_free (self->identifier); - self->identifier = new_identifier; - queue_notify (self, &self->identifier_idle_id, "identifier"); - } - else - { - g_free (new_identifier); - } - } - - static int - goa_kerberos_identity_compare (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - if (self->cached_verification_level < new_identity->cached_verification_level) - return -100; - - if (self->cached_verification_level > new_identity->cached_verification_level) - return 100; - - if (self->cached_verification_level != VERIFICATION_LEVEL_SIGNED_IN) - return 50; - - if (self->expiration_time < new_identity->expiration_time) - return -10; - - if (self->expiration_time > new_identity->expiration_time) - return 10; --- -2.39.0 - - -From 2f80c78f7b607fe45ac0c9cd5a9aa4dabecc9538 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 15 Dec 2022 16:27:27 -0500 -Subject: [PATCH 13/16] goakerberosidentity: Fix crash when erasing credentials - -Right now when erasing an identity we erase the -active credentials first and then the inactive -ones. - -We neglect to take the active one out of the hash -table, though, so it gets destroyed twice. - -This commit fixes that. ---- - src/goaidentity/goakerberosidentity.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index bc607966..46a6fb22 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1692,60 +1692,62 @@ goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) - { - goto free_principal; - } - - g_debug ("GoaKerberosIdentity: identity %s renewed", name); - renewed = TRUE; - - free_principal: - krb5_free_principal (self->kerberos_context, principal); - - out: - g_free (name); - - return renewed; - } - - gboolean - goa_kerberos_identity_erase (GoaKerberosIdentity *self, GError **error) - { - GHashTableIter iter; - const char *name; - krb5_ccache credentials_cache; - krb5_error_code error_code = 0; - - if (self->active_credentials_cache_name != NULL) - { - credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, - self->active_credentials_cache_name); - g_debug ("GoaKerberosIdentity: Destroying active credentials cache %s", self->active_credentials_cache_name); - error_code = krb5_cc_destroy (self->kerberos_context, credentials_cache); -+ g_hash_table_remove (self->credentials_caches, self->active_credentials_cache_name); -+ - g_clear_pointer (&self->active_credentials_cache_name, g_free); - - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, - error_code, _("Could not erase identity: ")); - } - } - - g_hash_table_iter_init (&iter, self->credentials_caches); - while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) - { - g_debug ("GoaKerberosIdentity: Destroying inactive credentials cache %s", name); - krb5_cc_destroy (self->kerberos_context, credentials_cache); - } - g_hash_table_remove_all (self->credentials_caches); - - return error_code == 0; - } - - GoaIdentity * - goa_kerberos_identity_new (krb5_context context, krb5_ccache cache, GError **error) - { - GoaKerberosIdentity *self; - krb5_ccache copied_cache; - - self = GOA_KERBEROS_IDENTITY (g_object_new (GOA_TYPE_KERBEROS_IDENTITY, NULL)); - self->kerberos_context = context; --- -2.39.0 - - -From 592aecd7bf439a7b5b276354d5262a1ce807d8e1 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 15 Dec 2022 15:35:49 -0500 -Subject: [PATCH 14/16] goakerberosidentity: Explicitly switch to credentials - cache when needed - -If we're updating a credentials cache and decide -it should be the new default for an identity, and -the old credentials cache was the default cache -for the cache collection then we should make the -new credential cache the default cache for the -collection, too. - -This commit adds that. It also makes the new -credentials cache the default if there wasn't a -valid default set already. This brings consistency -to differences in behavior from different kerberos -ccache types. ---- - src/goaidentity/goakerberosidentity.c | 63 +++++++++++++++++++++++++-- - 1 file changed, 60 insertions(+), 3 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 46a6fb22..4fe4b70b 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1527,100 +1527,157 @@ goa_kerberos_identity_compare (GoaKerberosIdentity *self, - if (self->cached_verification_level < new_identity->cached_verification_level) - return -100; - - if (self->cached_verification_level > new_identity->cached_verification_level) - return 100; - - if (self->cached_verification_level != VERIFICATION_LEVEL_SIGNED_IN) - return 50; - - if (self->expiration_time < new_identity->expiration_time) - return -10; - - if (self->expiration_time > new_identity->expiration_time) - return 10; - - if (self->start_time > new_identity->start_time) - return -5; - - if (self->start_time < new_identity->start_time) - return 5; - - if (self->renewal_time < new_identity->renewal_time) - return -1; - - if (self->renewal_time > new_identity->renewal_time) - return 1; - - return 0; - } - -+static char * -+get_default_cache_name (GoaKerberosIdentity *self) -+{ -+ int error_code; -+ krb5_ccache default_cache; -+ krb5_principal principal; -+ char *default_cache_name; -+ char *principal_name; -+ -+ error_code = krb5_cc_default (self->kerberos_context, &default_cache); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ /* Return NULL if the default cache doesn't pass basic sanity checks -+ */ -+ error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &principal_name); -+ krb5_free_principal (self->kerberos_context, principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ krb5_free_unparsed_name (self->kerberos_context, principal_name); -+ -+ default_cache_name = g_strdup (krb5_cc_get_name (self->kerberos_context, default_cache)); -+ krb5_cc_close (self->kerberos_context, default_cache); -+ -+ return default_cache_name; -+} -+ - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; - int comparison; - - G_LOCK (identity_lock); -+ -+ old_verification_level = self->cached_verification_level; -+ new_verification_level = new_identity->cached_verification_level; -+ - comparison = goa_kerberos_identity_compare (self, new_identity); - - if (new_identity->active_credentials_cache_name != NULL) - { -+ g_autofree char *default_cache_name = NULL; - krb5_ccache credentials_cache; - krb5_ccache copied_cache; -+ gboolean should_switch_to_new_credentials_cache = FALSE; -+ -+ default_cache_name = get_default_cache_name (self); -+ -+ if (default_cache_name == NULL) -+ should_switch_to_new_credentials_cache = TRUE; - - credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, - new_identity->active_credentials_cache_name); - krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - -+ if (g_strcmp0 (default_cache_name, self->active_credentials_cache_name) == 0) -+ { -+ if ((comparison < 0) || -+ (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN)) -+ should_switch_to_new_credentials_cache = TRUE; -+ } -+ - if (comparison < 0) -- g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ { -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ self->active_credentials_cache_name = g_strdup (new_identity->active_credentials_cache_name); -+ } - - goa_kerberos_identity_add_credentials_cache (self, copied_cache); -+ -+ if (should_switch_to_new_credentials_cache) -+ krb5_cc_switch (self->kerberos_context, copied_cache); - } - G_UNLOCK (identity_lock); - - clear_alarms (new_identity); - - if (comparison >= 0) - return; - - G_LOCK (identity_lock); - update_identifier (self, new_identity); - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); -- old_verification_level = self->cached_verification_level; -- new_verification_level = new_identity->cached_verification_level; - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - reset_alarms (self); - else - clear_alarms (self); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { - if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && - new_verification_level == VERIFICATION_LEVEL_EXISTS) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); - } - else if (old_verification_level == VERIFICATION_LEVEL_EXISTS && - new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - { - G_LOCK (identity_lock); --- -2.39.0 - - -From 5cedc418bb4eabd94085660f224ebe78532d3ed4 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Mon, 16 Jan 2023 15:00:36 -0500 -Subject: [PATCH 15/16] goakerberosidentity: Fix automatic reinitialization - -The identity service has the ability to automatically fetch a new ticket -granting ticket from the KDC when the existing one expires, provided the -user keeps their kerberos password in GNOME keyring. - -Unfortunately, commit aca400799c225a84e5d0fc90efb206c8f1d48bc3 -inadvertently broke this feature in some cases. - -When deciding whether or not to make a new credentials cache for a -principal the active one it looks at various characteristics of the -competing credentials to decide which cache is better. - -For instance, if one credentials cache has a ticket that's valid and -signed in, but the other credentials cache only has an expired ticket, -then obviously the one that's valid and signed in gets picked to be -active. - -Likewise, if one is expiring in 10 minutes and one is expiring in -24 hours, the one that expires in 24 hours will be treated as better. - -This comparison, only makes sense, though when looking at two different -credentials caches. If we're updating a preexisting credentials cache, -then we're actually just comparing up to date data with out of date -data. In that case, we need to proceed even if new newer view of the -credentials look worse than the older view of those credentials. -Unfortunately, the buggy commit neglected to account for that. - -This commit fixes that problem and related problems, by more -thoroughly and systematically checking all the permutations of -credentials in the old credentials cache for the identity, the -new credentials cache for the identity, and the default credentials -cache. It also adds a lot more logging for clarity. ---- - src/goaidentity/goakerberosidentity.c | 198 ++++++++++++++++++++++++-- - 1 file changed, 183 insertions(+), 15 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index 4fe4b70b..d046a8a4 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -1562,130 +1562,298 @@ get_default_cache_name (GoaKerberosIdentity *self) - krb5_principal principal; - char *default_cache_name; - char *principal_name; - - error_code = krb5_cc_default (self->kerberos_context, &default_cache); - - if (error_code != 0) - return NULL; - - /* Return NULL if the default cache doesn't pass basic sanity checks - */ - error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); - - if (error_code != 0) - return NULL; - - error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &principal_name); - krb5_free_principal (self->kerberos_context, principal); - - if (error_code != 0) - return NULL; - - krb5_free_unparsed_name (self->kerberos_context, principal_name); - - default_cache_name = g_strdup (krb5_cc_get_name (self->kerberos_context, default_cache)); - krb5_cc_close (self->kerberos_context, default_cache); - - return default_cache_name; - } - -+static char * -+get_default_principal (GoaKerberosIdentity *self) -+{ -+ int error_code; -+ krb5_ccache default_cache; -+ krb5_principal principal; -+ char *unparsed_principal, *principal_name; -+ -+ error_code = krb5_cc_default (self->kerberos_context, &default_cache); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ /* Return NULL if the default cache doesn't pass basic sanity checks -+ */ -+ error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &unparsed_principal); -+ krb5_free_principal (self->kerberos_context, principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ principal_name = g_strdup (unparsed_principal); -+ krb5_free_unparsed_name (self->kerberos_context, unparsed_principal); -+ -+ krb5_cc_close (self->kerberos_context, default_cache); -+ -+ return principal_name; -+} -+ - void - goa_kerberos_identity_update (GoaKerberosIdentity *self, - GoaKerberosIdentity *new_identity) - { - VerificationLevel old_verification_level, new_verification_level; -+ gboolean should_set_cache_active = FALSE; - gboolean time_changed = FALSE; - char *preauth_identity_source = NULL; -+ g_autofree char *default_principal = NULL; - int comparison; - - G_LOCK (identity_lock); - -+ g_debug ("GoaKerberosIdentity: Evaluating updated credentials for identity %s " -+ "(old credentials cache name: %s, new credentials cache name: %s)", -+ self->identifier, -+ self->active_credentials_cache_name, -+ new_identity->active_credentials_cache_name); - old_verification_level = self->cached_verification_level; - new_verification_level = new_identity->cached_verification_level; - -+ default_principal = get_default_principal (self); - comparison = goa_kerberos_identity_compare (self, new_identity); - - if (new_identity->active_credentials_cache_name != NULL) - { - g_autofree char *default_cache_name = NULL; - krb5_ccache credentials_cache; - krb5_ccache copied_cache; -- gboolean should_switch_to_new_credentials_cache = FALSE; -+ gboolean should_set_cache_as_default = FALSE; -+ gboolean is_default_principal = FALSE, is_default_cache = FALSE; -+ gboolean cache_already_active = FALSE; -+ -+ is_default_principal = g_strcmp0 (default_principal, self->identifier) == 0; - - default_cache_name = get_default_cache_name (self); -+ is_default_cache = g_strcmp0 (default_cache_name, self->active_credentials_cache_name) == 0; -+ cache_already_active = g_strcmp0 (self->active_credentials_cache_name, new_identity->active_credentials_cache_name) == 0; - -- if (default_cache_name == NULL) -- should_switch_to_new_credentials_cache = TRUE; -+ g_debug ("GoaKerberosIdentity: Default credentials cache is '%s' (is %sus, is %sactive)", default_cache_name, is_default_cache? "" : "not ", cache_already_active? "" : "not "); - -- credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, -- new_identity->active_credentials_cache_name); -- krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); -+ if (default_principal == NULL) -+ { -+ should_set_cache_as_default = TRUE; -+ should_set_cache_active = TRUE; - -- if (g_strcmp0 (default_cache_name, self->active_credentials_cache_name) == 0) -+ g_debug ("GoaKerberosIdentity: Setting default credentials cache to '%s' (principal %s) " -+ "because there is no active default", -+ new_identity->active_credentials_cache_name, -+ self->identifier); -+ } -+ else if (!is_default_principal) -+ { -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache from '%s' to '%s' (principal %s) " -+ "because identity is currently not default (credentials already active? %s)", -+ default_cache_name, -+ new_identity->active_credentials_cache_name, -+ self->identifier, -+ cache_already_active? "yes" : "no"); -+ should_set_cache_as_default = FALSE; -+ -+ if (comparison < 0) -+ { -+ should_set_cache_active = TRUE; -+ -+ g_debug ("GoaKerberosIdentity: Switching identity %s from credentials cache '%s' to credentials cache '%s' " -+ "because it has better credentials", -+ self->identifier, -+ self->active_credentials_cache_name, -+ new_identity->active_credentials_cache_name); -+ } -+ else if (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -+ { -+ should_set_cache_active = TRUE; -+ -+ g_debug ("GoaKerberosIdentity: Switching identity %s from credentials cache '%s' to " -+ "'%s' because it is newer and is otherwise just as good", -+ self->identifier, -+ self->active_credentials_cache_name, -+ new_identity->active_credentials_cache_name); -+ } -+ else -+ { -+ should_set_cache_active = FALSE; -+ -+ g_debug ("GoaKerberosIdentity: Not switching identity %s from credentials cache '%s' to '%s' " -+ "because it has less good credentials", -+ self->identifier, -+ default_cache_name, -+ new_identity->active_credentials_cache_name); -+ } -+ } -+ else if (cache_already_active) -+ { -+ if (is_default_cache) -+ { -+ should_set_cache_as_default = FALSE; -+ should_set_cache_active = FALSE; -+ -+ g_debug ("GoaKerberosIdentity: Not setting default credentials cache to '%s' " -+ "because cache is already active for identity %s and identity is default", -+ new_identity->active_credentials_cache_name, -+ self->identifier); -+ } -+ else -+ { -+ should_set_cache_as_default = TRUE; -+ should_set_cache_active = TRUE; -+ -+ g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -+ "because identity %s is default and cache is supposed to be active already but isn't", -+ default_cache_name, -+ new_identity->active_credentials_cache_name, -+ self->identifier); -+ } -+ } -+ else - { -- if ((comparison < 0) || -- (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN)) -- should_switch_to_new_credentials_cache = TRUE; -+ if (comparison < 0) -+ { -+ should_set_cache_as_default = TRUE; -+ should_set_cache_active = TRUE; -+ -+ g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -+ "because identity %s is default and the cache has better credentials than those " -+ "in '%s'", -+ default_cache_name, -+ new_identity->active_credentials_cache_name, -+ self->identifier, -+ self->active_credentials_cache_name); -+ } -+ else if (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -+ { -+ should_set_cache_as_default = TRUE; -+ should_set_cache_active = TRUE; -+ -+ g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -+ "because identity %s is default, and the cache has newer, and otherwise " -+ "just as good credentials as those in '%s'", -+ default_cache_name, -+ new_identity->active_credentials_cache_name, -+ self->identifier, -+ self->active_credentials_cache_name); -+ } -+ else -+ { -+ should_set_cache_as_default = FALSE; -+ should_set_cache_active = FALSE; -+ -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache from '%s' to '%s' " -+ "because identity %s is default but newer credentials aren't as good as those in '%s'", -+ default_cache_name, -+ new_identity->active_credentials_cache_name, -+ self->identifier, -+ self->active_credentials_cache_name); -+ } - } -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, -+ new_identity->active_credentials_cache_name); -+ krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); - -- if (comparison < 0) -+ if (should_set_cache_active) - { - g_clear_pointer (&self->active_credentials_cache_name, g_free); - self->active_credentials_cache_name = g_strdup (new_identity->active_credentials_cache_name); - } - - goa_kerberos_identity_add_credentials_cache (self, copied_cache); - -- if (should_switch_to_new_credentials_cache) -+ if (should_set_cache_as_default) - krb5_cc_switch (self->kerberos_context, copied_cache); - } - G_UNLOCK (identity_lock); - - clear_alarms (new_identity); - -- if (comparison >= 0) -+ if (!should_set_cache_active) - return; - - G_LOCK (identity_lock); -+ g_debug ("GoaKerberosIdentity: Setting identity %s to use updated credentials in credentials cache '%s'", -+ self->identifier, self->active_credentials_cache_name); - update_identifier (self, new_identity); - time_changed |= set_start_time (self, new_identity->start_time); - time_changed |= set_renewal_time (self, new_identity->renewal_time); - time_changed |= set_expiration_time (self, new_identity->expiration_time); - G_UNLOCK (identity_lock); - - if (time_changed) - { - if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) -- reset_alarms (self); -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials have updated times, resetting alarms", self->identifier); -+ reset_alarms (self); -+ } - else -- clear_alarms (self); -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials are now expired, clearing alarms", self->identifier); -+ clear_alarms (self); -+ } -+ } -+ else -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials do not have updated times, so not adjusting alarms", self->identifier); - } - - G_LOCK (identity_lock); - g_free (self->preauth_identity_source); - self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { - if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && - new_verification_level == VERIFICATION_LEVEL_EXISTS) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); - } - else if (old_verification_level == VERIFICATION_LEVEL_EXISTS && - new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); - } - else - { - G_LOCK (identity_lock); --- -2.39.0 - - -From 8910cc096115f51bc1b13839d3909557276e7145 Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Thu, 19 Jan 2023 11:31:14 -0500 -Subject: [PATCH 16/16] goakerberosidentity: Fall back to stale credentials if - active credentials get destroyed - -At the moment, the identity service doesn't recognize when a credentials -cache gets kdestroy'd explicitly by the user. It knows when a principal -is purged from all credential caches, but it doesn't know when a -specific cache is removed. - -This means it doesn't fall back properly to an older credential crash if -the active cache gets destroyed. - -This commit addresses that problem by reachitecting things a bit. - -Previously, cache updates were processed by creating a new transient -GoaKerberosIdentity and then merging it into an existing identity using -goa_kerberos_identity_update. Using a full blown GoaKerberosIdentity -object as a wrapper around a lone credentials cache is kind of a weird -pattern and doesn't facillate processing cache removal, since we can't -create a transient identity for what's not there. - -This commit exports goa_kerberos_identity_add_credentials_cache as -public api as an alternative to the merging transient identity flow. - -It also also adds a goa_kerberos_identity_refresh function to be called -after goa_kerberos_identity_add_credentials_cache is preformed for all -new caches. It handles merging in the new credentials from the updated -credentials caches, and also handles cache removal. - -A benefit of this new flow is much of the guts of -goa_kerberos_identity_update have now been moved to verify_identity -allowing for some code deduplication. Previously verify_identity was -only called at object construction time, though, so this commit adds -more locking to accomodate it get called while the identity is in use by -other threads. ---- - src/goaidentity/goakerberosidentity.c | 706 +++++++++---------- - src/goaidentity/goakerberosidentity.h | 8 +- - src/goaidentity/goakerberosidentitymanager.c | 132 ++-- - 3 files changed, 419 insertions(+), 427 deletions(-) - -diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c -index d046a8a4..b5cbcecd 100644 ---- a/src/goaidentity/goakerberosidentity.c -+++ b/src/goaidentity/goakerberosidentity.c -@@ -619,65 +619,62 @@ set_expiration_time (GoaKerberosIdentity *self, - self->expiration_time = expiration_time; - queue_notify (self, &self->expiration_time_idle_id, "expiration-timestamp"); - return TRUE; - } - return FALSE; - } - - static void - examine_credentials (GoaKerberosIdentity *self, - krb5_creds *credentials, - krb5_timestamp *start_time, - krb5_timestamp *renewal_time, - krb5_timestamp *expiration_time, - gboolean *are_expired) - { - krb5_timestamp credentials_start_time; - krb5_timestamp credentials_end_time; - krb5_timestamp current_time; - - G_LOCK (identity_lock); - - if (credentials->times.starttime != 0) - credentials_start_time = credentials->times.starttime; - else - credentials_start_time = credentials->times.authtime; - - *renewal_time = credentials->times.renew_till; - - credentials_end_time = credentials->times.endtime; - -- if (self->start_time == 0) -- *start_time = credentials_start_time; -- else -- *start_time = MIN (self->start_time, credentials_start_time); -- *expiration_time = MAX (credentials->times.endtime, self->expiration_time); -+ *start_time = credentials_start_time; -+ *expiration_time = credentials->times.endtime; - G_UNLOCK (identity_lock); - - current_time = get_current_time (self); - - if (current_time < credentials_start_time || - credentials_end_time <= current_time) - *are_expired = TRUE; - else - *are_expired = FALSE; - } - - static VerificationLevel - verify_identity_in_credentials_cache (GoaKerberosIdentity *self, - char **preauth_identity_source, - krb5_ccache credentials_cache, - krb5_timestamp *start_time, - krb5_timestamp *renewal_time, - krb5_timestamp *expiration_time, - GError **error) - { - krb5_principal principal = NULL; - krb5_cc_cursor cursor; - krb5_creds credentials; - krb5_error_code error_code; - VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED; - - g_debug ("GoaKerberosIdentity: Verifying identity in credentials cache '%s'", - krb5_cc_get_name (self->kerberos_context, credentials_cache)); - - error_code = krb5_cc_get_principal (self->kerberos_context, credentials_cache, &principal); -@@ -771,146 +768,395 @@ verify_identity_in_credentials_cache (GoaKerberosIdentity *self, - out: - switch (verification_level) - { - case VERIFICATION_LEVEL_EXISTS: - g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are out of date", - krb5_cc_get_name (self->kerberos_context, credentials_cache)); - break; - - case VERIFICATION_LEVEL_SIGNED_IN: - g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are valid", - krb5_cc_get_name (self->kerberos_context, credentials_cache)); - break; - - case VERIFICATION_LEVEL_UNVERIFIED: - g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' are missing", - krb5_cc_get_name (self->kerberos_context, credentials_cache)); - break; - - case VERIFICATION_LEVEL_ERROR: - default: - g_debug ("GoaKerberosIdentity: Credentials in credentials cache '%s' could not be validated", - krb5_cc_get_name (self->kerberos_context, credentials_cache)); - break; - } - - if (principal != NULL) - krb5_free_principal (self->kerberos_context, principal); - return verification_level; - } - -+static char * -+get_default_principal (GoaKerberosIdentity *self) -+{ -+ int error_code; -+ krb5_ccache default_cache; -+ krb5_principal principal; -+ char *unparsed_principal, *principal_name; -+ -+ error_code = krb5_cc_default (self->kerberos_context, &default_cache); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ /* Return NULL if the default cache doesn't pass basic sanity checks -+ */ -+ error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &unparsed_principal); -+ krb5_free_principal (self->kerberos_context, principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ principal_name = g_strdup (unparsed_principal); -+ krb5_free_unparsed_name (self->kerberos_context, unparsed_principal); -+ -+ krb5_cc_close (self->kerberos_context, default_cache); -+ -+ return principal_name; -+} -+ -+static char * -+get_default_cache_name (GoaKerberosIdentity *self) -+{ -+ int error_code; -+ krb5_ccache default_cache; -+ krb5_principal principal; -+ char *default_cache_name; -+ char *principal_name; -+ -+ error_code = krb5_cc_default (self->kerberos_context, &default_cache); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ /* Return NULL if the default cache doesn't pass basic sanity checks -+ */ -+ error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &principal_name); -+ krb5_free_principal (self->kerberos_context, principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ krb5_free_unparsed_name (self->kerberos_context, principal_name); -+ -+ default_cache_name = g_strdup (krb5_cc_get_name (self->kerberos_context, default_cache)); -+ krb5_cc_close (self->kerberos_context, default_cache); -+ -+ return default_cache_name; -+} -+ - static VerificationLevel - verify_identity (GoaKerberosIdentity *self, - char **preauth_identity_source, - GError **error) - { - krb5_ccache credentials_cache; -+ g_autofree char *default_principal = NULL; -+ g_autofree char *default_credentials_cache_name = NULL; -+ gboolean is_default_principal; -+ gboolean is_default_credentials_cache; -+ gboolean should_switch_default_credentials_cache = FALSE; -+ gboolean time_changed = FALSE; - const char *name; -- krb5_timestamp start_time = 0; -- krb5_timestamp renewal_time = 0; -- krb5_timestamp expiration_time = 0; -- VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED; -+ krb5_timestamp best_start_time = 0; -+ krb5_timestamp best_renewal_time = 0; -+ krb5_timestamp best_expiration_time = 0; -+ g_autofree char *best_preauth_identity_source = NULL; -+ g_autofree char *best_credentials_cache_name = NULL; -+ VerificationLevel old_verification_level = VERIFICATION_LEVEL_UNVERIFIED; -+ VerificationLevel best_verification_level = VERIFICATION_LEVEL_UNVERIFIED; - GHashTableIter iter; - - if (self->active_credentials_cache_name != NULL) - { -+ G_LOCK (identity_lock); - credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, - self->active_credentials_cache_name); -+ G_UNLOCK (identity_lock); - -- verification_level = verify_identity_in_credentials_cache (self, -- preauth_identity_source, -- credentials_cache, -- &start_time, -- &renewal_time, -- &expiration_time, -- error); -- if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) -+ best_verification_level = verify_identity_in_credentials_cache (self, -+ &best_preauth_identity_source, -+ credentials_cache, -+ &best_start_time, -+ &best_renewal_time, -+ &best_expiration_time, -+ error); -+ G_LOCK (identity_lock); -+ best_credentials_cache_name = g_strdup (self->active_credentials_cache_name); -+ G_UNLOCK (identity_lock); -+ -+ if (best_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - goto out; - -- if (verification_level == VERIFICATION_LEVEL_UNVERIFIED) -+ if (best_verification_level == VERIFICATION_LEVEL_UNVERIFIED || -+ best_verification_level == VERIFICATION_LEVEL_ERROR) - { -- krb5_cc_close (self->kerberos_context, credentials_cache); -- g_hash_table_remove (self->credentials_caches, self->active_credentials_cache_name); -- g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ g_clear_pointer (&best_credentials_cache_name, g_free); -+ -+ G_LOCK (identity_lock); -+ if (self->identifier != NULL) -+ { -+ krb5_cc_close (self->kerberos_context, credentials_cache); -+ g_hash_table_remove (self->credentials_caches, self->active_credentials_cache_name); -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ } -+ G_UNLOCK (identity_lock); - } - } - -- self->start_time = 0; -- self->renewal_time = 0; -- self->expiration_time = 0; -+ G_LOCK (identity_lock); -+ old_verification_level = self->cached_verification_level; -+ G_UNLOCK (identity_lock); - -+ G_LOCK (identity_lock); - g_hash_table_iter_init (&iter, self->credentials_caches); - while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) - { - krb5_timestamp new_start_time = 0; - krb5_timestamp new_renewal_time = 0; - krb5_timestamp new_expiration_time = 0; -+ g_autofree char *new_preauth_identity_source = NULL; -+ VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED; -+ gboolean has_better_credentials = FALSE; - - if (g_strcmp0 (name, self->active_credentials_cache_name) == 0) - continue; - -- g_clear_pointer (preauth_identity_source, g_free); -+ G_UNLOCK (identity_lock); -+ -+ if (preauth_identity_source != NULL) -+ g_clear_pointer (preauth_identity_source, g_free); -+ - verification_level = verify_identity_in_credentials_cache (self, -- preauth_identity_source, -+ &new_preauth_identity_source, - credentials_cache, - &new_start_time, - &new_renewal_time, - &new_expiration_time, - error); - -- if (verification_level == VERIFICATION_LEVEL_SIGNED_IN || -- self->active_credentials_cache_name == NULL) -+ if (verification_level == VERIFICATION_LEVEL_UNVERIFIED || -+ verification_level == VERIFICATION_LEVEL_ERROR) - { -- g_clear_pointer (&self->active_credentials_cache_name, g_free); -- self->active_credentials_cache_name = g_strdup (name); -- start_time = new_start_time; -- renewal_time = new_renewal_time; -- expiration_time = new_expiration_time; -- -- if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) -- break; -+ G_LOCK (identity_lock); -+ if (self->identifier != NULL) -+ { -+ krb5_cc_close (self->kerberos_context, credentials_cache); -+ g_hash_table_iter_remove (&iter); -+ } -+ -+ /* Note: The lock is held while iterating */ -+ continue; - } -- else if (verification_level == VERIFICATION_LEVEL_UNVERIFIED) -+ -+ if (best_verification_level < verification_level) -+ has_better_credentials = TRUE; -+ else if (best_verification_level > verification_level) -+ has_better_credentials = FALSE; -+ else if (best_expiration_time < new_expiration_time) -+ has_better_credentials = TRUE; -+ else if (best_expiration_time > new_expiration_time) -+ has_better_credentials = FALSE; -+ else if (best_start_time > new_start_time) -+ has_better_credentials = TRUE; -+ else if (best_start_time > new_start_time) -+ has_better_credentials = FALSE; -+ else if (best_renewal_time < new_renewal_time) -+ has_better_credentials = TRUE; -+ else if (best_renewal_time > new_renewal_time) -+ has_better_credentials = FALSE; -+ else -+ has_better_credentials = FALSE; -+ -+ if (has_better_credentials) - { -- krb5_cc_close (self->kerberos_context, credentials_cache); -- g_hash_table_iter_remove (&iter); -+ best_verification_level = verification_level; -+ best_start_time = new_start_time; -+ best_renewal_time = new_renewal_time; -+ best_expiration_time = new_expiration_time; -+ -+ g_clear_pointer (&best_preauth_identity_source, g_free); -+ best_preauth_identity_source = g_steal_pointer (&new_preauth_identity_source); -+ -+ g_clear_pointer (&best_credentials_cache_name, g_free); -+ best_credentials_cache_name = g_strdup (name); - } -+ -+ G_LOCK (identity_lock); -+ } -+ G_UNLOCK (identity_lock); -+ -+ if (best_credentials_cache_name == NULL) -+ { -+ g_hash_table_iter_init (&iter, self->credentials_caches); -+ if (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) -+ best_credentials_cache_name = g_strdup (name); - } - - out: -+ - G_LOCK (identity_lock); -- set_start_time (self, start_time); -- set_renewal_time (self, renewal_time); -- set_expiration_time (self, expiration_time); -+ g_clear_pointer (&self->active_credentials_cache_name, g_free); -+ self->active_credentials_cache_name = g_steal_pointer (&best_credentials_cache_name); - G_UNLOCK (identity_lock); - -- return verification_level; -+ *preauth_identity_source = g_steal_pointer (&best_preauth_identity_source); -+ -+ if (best_verification_level > VERIFICATION_LEVEL_UNVERIFIED) -+ { -+ G_LOCK (identity_lock); -+ time_changed |= set_start_time (self, best_start_time); -+ time_changed |= set_renewal_time (self, best_renewal_time); -+ time_changed |= set_expiration_time (self, best_expiration_time); -+ G_UNLOCK (identity_lock); -+ -+ if (time_changed) -+ { -+ if (best_verification_level == VERIFICATION_LEVEL_SIGNED_IN) -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials have updated times, resetting alarms", self->identifier); -+ reset_alarms (self); -+ } -+ else -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials are now expired, clearing alarms", self->identifier); -+ clear_alarms (self); -+ } -+ } -+ else -+ { -+ g_debug ("GoaKerberosIdentity: identity %s credentials do not have updated times, so not adjusting alarms", self->identifier); -+ } -+ } -+ else -+ { -+ g_debug ("GoaKerberosIdentity: identity is unverified, clearing alarms"); -+ clear_alarms (self); -+ } -+ -+ if (best_verification_level != old_verification_level) -+ { -+ if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && -+ best_verification_level == VERIFICATION_LEVEL_EXISTS) -+ { -+ G_LOCK (identity_lock); -+ self->cached_verification_level = best_verification_level; -+ G_UNLOCK (identity_lock); -+ -+ g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); -+ } -+ else if (old_verification_level == VERIFICATION_LEVEL_EXISTS && -+ best_verification_level == VERIFICATION_LEVEL_SIGNED_IN) -+ { -+ G_LOCK (identity_lock); -+ self->cached_verification_level = best_verification_level; -+ G_UNLOCK (identity_lock); -+ -+ g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); -+ } -+ else -+ { -+ G_LOCK (identity_lock); -+ self->cached_verification_level = best_verification_level; -+ G_UNLOCK (identity_lock); -+ } -+ queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); -+ } -+ -+ default_principal = get_default_principal (self); -+ is_default_principal = g_strcmp0 (default_principal, self->identifier) == 0; -+ -+ default_credentials_cache_name = get_default_cache_name (self); -+ is_default_credentials_cache = g_strcmp0 (default_credentials_cache_name, self->active_credentials_cache_name) == 0; -+ -+ if (self->active_credentials_cache_name == NULL) -+ { -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache because identity %s has no active credentials cache to switch to", self->identifier); -+ should_switch_default_credentials_cache = FALSE; -+ } -+ else if (self->identifier == NULL) -+ { -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache to '%s' because it is not yet initialized", self->active_credentials_cache_name); -+ should_switch_default_credentials_cache = FALSE; -+ } -+ else if (default_principal == NULL) -+ { -+ g_debug ("GoaKerberosIdentity: Switching default credentials cache to '%s' (identity %s) because there is currently no default", self->active_credentials_cache_name, self->identifier); -+ should_switch_default_credentials_cache = TRUE; -+ } -+ else if (!is_default_principal) -+ { -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache because identity %s is not the default identity", self->identifier); -+ should_switch_default_credentials_cache = FALSE; -+ } -+ else if (!is_default_credentials_cache) -+ { -+ g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' because identity %s is the default, and that credentials cache is supposed to be the active cache for that identity", -+ default_credentials_cache_name, self->active_credentials_cache_name, self->identifier); -+ should_switch_default_credentials_cache = TRUE; -+ } -+ else -+ { -+ g_debug ("GoaKerberosIdentity: Not switching default credentials cache to '%s' for identity %s because it's already the default", self->active_credentials_cache_name, self->identifier); -+ should_switch_default_credentials_cache = FALSE; -+ } -+ -+ if (should_switch_default_credentials_cache) -+ { -+ G_LOCK (identity_lock); -+ credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, -+ self->active_credentials_cache_name); -+ krb5_cc_switch (self->kerberos_context, credentials_cache); -+ G_UNLOCK (identity_lock); -+ } -+ -+ return best_verification_level; - } - - static gboolean - goa_kerberos_identity_is_signed_in (GoaIdentity *identity) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (identity); - gboolean is_signed_in = FALSE; - - G_LOCK (identity_lock); - if (self->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - is_signed_in = TRUE; - G_UNLOCK (identity_lock); - - return is_signed_in; - } - - static void - identity_interface_init (GoaIdentityInterface *interface) - { - interface->get_identifier = goa_kerberos_identity_get_identifier; - interface->is_signed_in = goa_kerberos_identity_is_signed_in; - } - - static void - on_expiration_alarm_fired (GoaAlarm *alarm, - GoaKerberosIdentity *self) - { - g_return_if_fail (GOA_IS_ALARM (alarm)); - g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); - -@@ -1059,60 +1305,62 @@ reset_alarms (GoaKerberosIdentity *self) - GDateTime *expiring_time = NULL; - GDateTime *latest_possible_renewal_time = NULL; - GDateTime *renewal_time = NULL; - - G_LOCK (identity_lock); - start_time = g_date_time_new_from_unix_local (self->start_time); - if (self->renewal_time != 0) - latest_possible_renewal_time = g_date_time_new_from_unix_local (self->renewal_time); - expiration_time = g_date_time_new_from_unix_local (self->expiration_time); - G_UNLOCK (identity_lock); - - /* Let the user reauthenticate 10 min before expiration */ - expiring_time = g_date_time_add_minutes (expiration_time, -10); - - if (latest_possible_renewal_time != NULL) - { - GTimeSpan lifespan; - - lifespan = g_date_time_difference (expiration_time, start_time); - - /* Try to quietly auto-renew halfway through so in ideal configurations - * the ticket is never more than halfway to unrenewable - */ - renewal_time = g_date_time_add (start_time, lifespan / 2); - } - - disconnect_alarm_signals (self); - - if (renewal_time != NULL) - reset_alarm (self, &self->renewal_alarm, renewal_time); -+ else if (self->renewal_alarm != NULL) -+ clear_alarm_and_unref_on_idle (self, &self->renewal_alarm); - - reset_alarm (self, &self->expiring_alarm, expiring_time); - reset_alarm (self, &self->expiration_alarm, expiration_time); - - g_clear_pointer (&expiring_time, g_date_time_unref); - g_clear_pointer (&renewal_time, g_date_time_unref); - g_clear_pointer (&expiration_time, g_date_time_unref); - g_clear_pointer (&latest_possible_renewal_time, g_date_time_unref); - g_clear_pointer (&start_time, g_date_time_unref); - - connect_alarm_signals (self); - } - - static void - clear_alarms (GoaKerberosIdentity *self) - { - disconnect_alarm_signals (self); - clear_alarm_and_unref_on_idle (self, &self->renewal_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiring_alarm); - clear_alarm_and_unref_on_idle (self, &self->expiration_alarm); - } - - static gboolean - goa_kerberos_identity_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) - { - GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable); - GError *verification_error; - -@@ -1185,114 +1433,137 @@ on_kerberos_inquiry (krb5_context kerberos_context, - GoaIdentityInquiry *inquiry; - krb5_error_code error_code = 0; - - if (number_of_prompts == 0) - goto out; - - inquiry = goa_kerberos_identity_inquiry_new (operation->identity, - name, - banner, - prompts, - number_of_prompts); - - operation->inquiry_func (inquiry, - operation->cancellable, - operation->inquiry_data); - - if (goa_identity_inquiry_is_failed (inquiry)) - error_code = KRB5_LIBOS_CANTREADPWD; - else if (!goa_identity_inquiry_is_complete (inquiry)) - g_cancellable_cancel (operation->cancellable); - - if (g_cancellable_is_cancelled (operation->cancellable)) - error_code = KRB5_LIBOS_PWDINTR; - - g_object_unref (inquiry); - - out: - return error_code; - } - --static void -+gboolean -+goa_kerberos_identity_has_credentials_cache (GoaKerberosIdentity *self, -+ krb5_ccache credentials_cache) -+{ -+ const char *cache_name; -+ -+ cache_name = krb5_cc_get_name (self->kerberos_context, credentials_cache); -+ -+ return g_hash_table_contains (self->credentials_caches, cache_name); -+} -+ -+void - goa_kerberos_identity_add_credentials_cache (GoaKerberosIdentity *self, - krb5_ccache credentials_cache) - { - const char *cache_name; -+ krb5_ccache copied_cache; - - cache_name = krb5_cc_get_name (self->kerberos_context, credentials_cache); - - if (g_hash_table_contains (self->credentials_caches, cache_name)) - { - krb5_ccache old_credentials_cache; - -+ g_debug ("GoaKerberosIdentity: Updating credentials in credentials cache '%s' for identity %s ", cache_name, self->identifier); -+ - old_credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, cache_name); - - krb5_cc_close (self->kerberos_context, old_credentials_cache); - } -+ else -+ { -+ if (self->identifier != NULL) -+ g_debug ("GoaKerberosIdentity: Associating identity %s with new credentials cache '%s'", self->identifier, cache_name); -+ else -+ g_debug ("GoaKerberosIdentity: Associating new identity with new credentials cache '%s'", cache_name); -+ } - -- g_hash_table_replace (self->credentials_caches, g_strdup (cache_name), credentials_cache); -+ krb5_cc_dup (self->kerberos_context, credentials_cache, &copied_cache); -+ g_hash_table_replace (self->credentials_caches, g_strdup (cache_name), copied_cache); - - if (self->active_credentials_cache_name == NULL) - { - self->active_credentials_cache_name = g_strdup (cache_name); - } - } - - static gboolean - create_credentials_cache (GoaKerberosIdentity *self, - GError **error) - { - krb5_ccache default_cache; - krb5_ccache new_cache; - const char *cache_type; - krb5_error_code error_code; - - error_code = krb5_cc_default (self->kerberos_context, &default_cache); - - if (error_code == 0) - { - cache_type = krb5_cc_get_type (self->kerberos_context, default_cache); - error_code = krb5_cc_new_unique (self->kerberos_context, cache_type, NULL, &new_cache); - } - - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS, - error_code, - _("Could not create credential cache: ")); - - return FALSE; - } - - goa_kerberos_identity_add_credentials_cache (self, new_cache); -+ krb5_cc_close (self->kerberos_context, new_cache); - - return TRUE; - } - - static gboolean - goa_kerberos_identity_update_credentials (GoaKerberosIdentity *self, - krb5_principal principal, - krb5_creds *new_credentials, - GError **error) - { - krb5_error_code error_code; - krb5_ccache credentials_cache; - - - if (self->active_credentials_cache_name == NULL) - { - if (!create_credentials_cache (self, error)) - { - krb5_free_cred_contents (self->kerberos_context, new_credentials); - goto out; - } - } - - credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, - self->active_credentials_cache_name); - - error_code = krb5_cc_initialize (self->kerberos_context, credentials_cache, principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, -@@ -1348,81 +1619,78 @@ sign_in_operation_new (GoaKerberosIdentity *identity, - } - - static void - sign_in_operation_free (SignInOperation *operation) - { - g_object_unref (operation->identity); - g_object_unref (operation->cancellable); - - g_slice_free (SignInOperation, operation); - } - - gboolean - goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, - const char *principal_name, - gconstpointer initial_password, - const char *preauth_source, - GoaIdentitySignInFlags flags, - GoaIdentityInquiryFunc inquiry_func, - gpointer inquiry_data, - GDestroyNotify destroy_notify, - GCancellable *cancellable, - GError **error) - { - SignInOperation *operation; - krb5_principal principal; - krb5_error_code error_code; - krb5_creds new_credentials; - krb5_get_init_creds_opt *options; - krb5_deltat start_time; - char *service_name; -- gboolean signed_in; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - error_code = krb5_get_init_creds_opt_alloc (self->kerberos_context, &options); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS, - error_code, - "%s", - ""); /* Silence -Wformat-zero-length */ - if (destroy_notify) - destroy_notify (inquiry_data); - return FALSE; - } - -- signed_in = FALSE; -- - operation = sign_in_operation_new (self, - inquiry_func, - inquiry_data, - destroy_notify, - cancellable); - - if (g_strcmp0 (self->identifier, principal_name) != 0) - { - g_free (self->identifier); - self->identifier = g_strdup (principal_name); - } - - error_code = krb5_parse_name (self->kerberos_context, principal_name, &principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_PARSING_IDENTIFIER, - error_code, - "%s", - ""); /* Silence -Wformat-zero-length */ - if (destroy_notify) - destroy_notify (inquiry_data); - return FALSE; - } - - if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING) == 0) - krb5_get_init_creds_opt_set_forwardable (options, TRUE); - - if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING) == 0) -@@ -1468,426 +1736,128 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_AUTHENTICATION_FAILED, - error_code, - "%s", - ""); /* Silence -Wformat-zero-length */ - if (destroy_notify) - destroy_notify (inquiry_data); - sign_in_operation_free (operation); - - krb5_free_principal (self->kerberos_context, principal); - goto done; - } - - if (destroy_notify) - destroy_notify (inquiry_data); - sign_in_operation_free (operation); - - if (!goa_kerberos_identity_update_credentials (self, - principal, - &new_credentials, - error)) - { - krb5_free_principal (self->kerberos_context, principal); - goto done; - } - krb5_free_principal (self->kerberos_context, principal); - -- g_debug ("GoaKerberosIdentity: identity signed in"); -- signed_in = TRUE; - done: - -- return signed_in; --} -- --static void --update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity) --{ -- char *new_identifier; -+ goa_kerberos_identity_refresh (self); - -- new_identifier = get_identifier (new_identity, NULL); -- if (g_strcmp0 (self->identifier, new_identifier) != 0 && new_identifier != NULL) -- { -- g_free (self->identifier); -- self->identifier = new_identifier; -- queue_notify (self, &self->identifier_idle_id, "identifier"); -- } -- else -+ if (self->cached_verification_level != VERIFICATION_LEVEL_SIGNED_IN) - { -- g_free (new_identifier); -+ g_debug ("GoaKerberosIdentity: Identity '%s' could not be signed in", principal_name); -+ return FALSE; - } --} -- --static int --goa_kerberos_identity_compare (GoaKerberosIdentity *self, -- GoaKerberosIdentity *new_identity) --{ -- if (self->cached_verification_level < new_identity->cached_verification_level) -- return -100; -- -- if (self->cached_verification_level > new_identity->cached_verification_level) -- return 100; -- -- if (self->cached_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -- return 50; -- -- if (self->expiration_time < new_identity->expiration_time) -- return -10; -- -- if (self->expiration_time > new_identity->expiration_time) -- return 10; -- -- if (self->start_time > new_identity->start_time) -- return -5; -- -- if (self->start_time < new_identity->start_time) -- return 5; -- -- if (self->renewal_time < new_identity->renewal_time) -- return -1; -- -- if (self->renewal_time > new_identity->renewal_time) -- return 1; -- -- return 0; --} -- --static char * --get_default_cache_name (GoaKerberosIdentity *self) --{ -- int error_code; -- krb5_ccache default_cache; -- krb5_principal principal; -- char *default_cache_name; -- char *principal_name; -- -- error_code = krb5_cc_default (self->kerberos_context, &default_cache); -- -- if (error_code != 0) -- return NULL; -- -- /* Return NULL if the default cache doesn't pass basic sanity checks -- */ -- error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -- -- if (error_code != 0) -- return NULL; -- -- error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &principal_name); -- krb5_free_principal (self->kerberos_context, principal); -- -- if (error_code != 0) -- return NULL; -- -- krb5_free_unparsed_name (self->kerberos_context, principal_name); -- -- default_cache_name = g_strdup (krb5_cc_get_name (self->kerberos_context, default_cache)); -- krb5_cc_close (self->kerberos_context, default_cache); -- -- return default_cache_name; --} -- --static char * --get_default_principal (GoaKerberosIdentity *self) --{ -- int error_code; -- krb5_ccache default_cache; -- krb5_principal principal; -- char *unparsed_principal, *principal_name; -- -- error_code = krb5_cc_default (self->kerberos_context, &default_cache); -- -- if (error_code != 0) -- return NULL; -- -- /* Return NULL if the default cache doesn't pass basic sanity checks -- */ -- error_code = krb5_cc_get_principal (self->kerberos_context, default_cache, &principal); -- -- if (error_code != 0) -- return NULL; -- -- error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &unparsed_principal); -- krb5_free_principal (self->kerberos_context, principal); -- -- if (error_code != 0) -- return NULL; -- -- principal_name = g_strdup (unparsed_principal); -- krb5_free_unparsed_name (self->kerberos_context, unparsed_principal); -- -- krb5_cc_close (self->kerberos_context, default_cache); - -- return principal_name; -+ g_debug ("GoaKerberosIdentity: Identity '%s' signed in", principal_name); -+ return TRUE; - } - - void --goa_kerberos_identity_update (GoaKerberosIdentity *self, -- GoaKerberosIdentity *new_identity) -+goa_kerberos_identity_refresh (GoaKerberosIdentity *self) - { - VerificationLevel old_verification_level, new_verification_level; -- gboolean should_set_cache_active = FALSE; -- gboolean time_changed = FALSE; -- char *preauth_identity_source = NULL; -- g_autofree char *default_principal = NULL; -- int comparison; -+ g_autofree char *preauth_identity_source = NULL; -+ g_autoptr (GError) error = NULL; - -- G_LOCK (identity_lock); -- -- g_debug ("GoaKerberosIdentity: Evaluating updated credentials for identity %s " -- "(old credentials cache name: %s, new credentials cache name: %s)", -+ g_debug ("GoaKerberosIdentity: Refreshing identity %s (active credentials cache: %s)", - self->identifier, -- self->active_credentials_cache_name, -- new_identity->active_credentials_cache_name); -- old_verification_level = self->cached_verification_level; -- new_verification_level = new_identity->cached_verification_level; -- -- default_principal = get_default_principal (self); -- comparison = goa_kerberos_identity_compare (self, new_identity); -- -- if (new_identity->active_credentials_cache_name != NULL) -- { -- g_autofree char *default_cache_name = NULL; -- krb5_ccache credentials_cache; -- krb5_ccache copied_cache; -- gboolean should_set_cache_as_default = FALSE; -- gboolean is_default_principal = FALSE, is_default_cache = FALSE; -- gboolean cache_already_active = FALSE; -- -- is_default_principal = g_strcmp0 (default_principal, self->identifier) == 0; -- -- default_cache_name = get_default_cache_name (self); -- is_default_cache = g_strcmp0 (default_cache_name, self->active_credentials_cache_name) == 0; -- cache_already_active = g_strcmp0 (self->active_credentials_cache_name, new_identity->active_credentials_cache_name) == 0; -- -- g_debug ("GoaKerberosIdentity: Default credentials cache is '%s' (is %sus, is %sactive)", default_cache_name, is_default_cache? "" : "not ", cache_already_active? "" : "not "); -- -- if (default_principal == NULL) -- { -- should_set_cache_as_default = TRUE; -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Setting default credentials cache to '%s' (principal %s) " -- "because there is no active default", -- new_identity->active_credentials_cache_name, -- self->identifier); -- } -- else if (!is_default_principal) -- { -- g_debug ("GoaKerberosIdentity: Not switching default credentials cache from '%s' to '%s' (principal %s) " -- "because identity is currently not default (credentials already active? %s)", -- default_cache_name, -- new_identity->active_credentials_cache_name, -- self->identifier, -- cache_already_active? "yes" : "no"); -- should_set_cache_as_default = FALSE; -- -- if (comparison < 0) -- { -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Switching identity %s from credentials cache '%s' to credentials cache '%s' " -- "because it has better credentials", -- self->identifier, -- self->active_credentials_cache_name, -- new_identity->active_credentials_cache_name); -- } -- else if (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -- { -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Switching identity %s from credentials cache '%s' to " -- "'%s' because it is newer and is otherwise just as good", -- self->identifier, -- self->active_credentials_cache_name, -- new_identity->active_credentials_cache_name); -- } -- else -- { -- should_set_cache_active = FALSE; -- -- g_debug ("GoaKerberosIdentity: Not switching identity %s from credentials cache '%s' to '%s' " -- "because it has less good credentials", -- self->identifier, -- default_cache_name, -- new_identity->active_credentials_cache_name); -- } -- } -- else if (cache_already_active) -- { -- if (is_default_cache) -- { -- should_set_cache_as_default = FALSE; -- should_set_cache_active = FALSE; -- -- g_debug ("GoaKerberosIdentity: Not setting default credentials cache to '%s' " -- "because cache is already active for identity %s and identity is default", -- new_identity->active_credentials_cache_name, -- self->identifier); -- } -- else -- { -- should_set_cache_as_default = TRUE; -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -- "because identity %s is default and cache is supposed to be active already but isn't", -- default_cache_name, -- new_identity->active_credentials_cache_name, -- self->identifier); -- } -- } -- else -- { -- if (comparison < 0) -- { -- should_set_cache_as_default = TRUE; -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -- "because identity %s is default and the cache has better credentials than those " -- "in '%s'", -- default_cache_name, -- new_identity->active_credentials_cache_name, -- self->identifier, -- self->active_credentials_cache_name); -- } -- else if (comparison == 0 && old_verification_level != VERIFICATION_LEVEL_SIGNED_IN) -- { -- should_set_cache_as_default = TRUE; -- should_set_cache_active = TRUE; -- -- g_debug ("GoaKerberosIdentity: Switching default credentials cache from '%s' to '%s' " -- "because identity %s is default, and the cache has newer, and otherwise " -- "just as good credentials as those in '%s'", -- default_cache_name, -- new_identity->active_credentials_cache_name, -- self->identifier, -- self->active_credentials_cache_name); -- } -- else -- { -- should_set_cache_as_default = FALSE; -- should_set_cache_active = FALSE; -- -- g_debug ("GoaKerberosIdentity: Not switching default credentials cache from '%s' to '%s' " -- "because identity %s is default but newer credentials aren't as good as those in '%s'", -- default_cache_name, -- new_identity->active_credentials_cache_name, -- self->identifier, -- self->active_credentials_cache_name); -- } -- } -- credentials_cache = (krb5_ccache) g_hash_table_lookup (new_identity->credentials_caches, -- new_identity->active_credentials_cache_name); -- krb5_cc_dup (new_identity->kerberos_context, credentials_cache, &copied_cache); -- -- if (should_set_cache_active) -- { -- g_clear_pointer (&self->active_credentials_cache_name, g_free); -- self->active_credentials_cache_name = g_strdup (new_identity->active_credentials_cache_name); -- } -+ self->active_credentials_cache_name); - -- goa_kerberos_identity_add_credentials_cache (self, copied_cache); -- -- if (should_set_cache_as_default) -- krb5_cc_switch (self->kerberos_context, copied_cache); -- } -+ G_LOCK (identity_lock); -+ old_verification_level = self->cached_verification_level; - G_UNLOCK (identity_lock); - -- clear_alarms (new_identity); -- -- if (!should_set_cache_active) -- return; -+ new_verification_level = verify_identity (self, &preauth_identity_source, &error); - - G_LOCK (identity_lock); -- g_debug ("GoaKerberosIdentity: Setting identity %s to use updated credentials in credentials cache '%s'", -- self->identifier, self->active_credentials_cache_name); -- update_identifier (self, new_identity); -- time_changed |= set_start_time (self, new_identity->start_time); -- time_changed |= set_renewal_time (self, new_identity->renewal_time); -- time_changed |= set_expiration_time (self, new_identity->expiration_time); -- G_UNLOCK (identity_lock); -- -- if (time_changed) -- { -- if (new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) -- { -- g_debug ("GoaKerberosIdentity: identity %s credentials have updated times, resetting alarms", self->identifier); -- reset_alarms (self); -- } -- else -- { -- g_debug ("GoaKerberosIdentity: identity %s credentials are now expired, clearing alarms", self->identifier); -- clear_alarms (self); -- } -- } -- else -+ if (g_strcmp0 (self->preauth_identity_source, preauth_identity_source) != 0) - { -- g_debug ("GoaKerberosIdentity: identity %s credentials do not have updated times, so not adjusting alarms", self->identifier); -+ g_free (self->preauth_identity_source); -+ self->preauth_identity_source = g_steal_pointer (&preauth_identity_source); - } -- -- G_LOCK (identity_lock); -- g_free (self->preauth_identity_source); -- self->preauth_identity_source = preauth_identity_source; - G_UNLOCK (identity_lock); - - if (new_verification_level != old_verification_level) - { -- if (old_verification_level == VERIFICATION_LEVEL_SIGNED_IN && -+ if ((old_verification_level == VERIFICATION_LEVEL_SIGNED_IN) && - new_verification_level == VERIFICATION_LEVEL_EXISTS) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); - } - else if (old_verification_level == VERIFICATION_LEVEL_EXISTS && - new_verification_level == VERIFICATION_LEVEL_SIGNED_IN) - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - - g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); - } - else - { - G_LOCK (identity_lock); - self->cached_verification_level = new_verification_level; - G_UNLOCK (identity_lock); - } -+ G_LOCK (identity_lock); - queue_notify (self, &self->is_signed_in_idle_id, "is-signed-in"); -+ G_UNLOCK (identity_lock); - } - } - - gboolean - goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) - { - krb5_error_code error_code = 0; - krb5_principal principal; - krb5_creds new_credentials; - krb5_ccache credentials_cache; - gboolean renewed = FALSE; - char *name = NULL; - - if (self->active_credentials_cache_name == NULL) - { - g_set_error (error, - GOA_IDENTITY_ERROR, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, - _("Not signed in")); - goto out; - } - - credentials_cache = (krb5_ccache) g_hash_table_lookup (self->credentials_caches, - self->active_credentials_cache_name); - error_code = krb5_cc_get_principal (self->kerberos_context, credentials_cache, &principal); - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, -@@ -1945,47 +1915,45 @@ goa_kerberos_identity_erase (GoaKerberosIdentity *self, GError **error) - g_debug ("GoaKerberosIdentity: Destroying active credentials cache %s", self->active_credentials_cache_name); - error_code = krb5_cc_destroy (self->kerberos_context, credentials_cache); - g_hash_table_remove (self->credentials_caches, self->active_credentials_cache_name); - - g_clear_pointer (&self->active_credentials_cache_name, g_free); - - if (error_code != 0) - { - set_and_prefix_error_from_krb5_error_code (self, - error, - GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, - error_code, _("Could not erase identity: ")); - } - } - - g_hash_table_iter_init (&iter, self->credentials_caches); - while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &credentials_cache)) - { - g_debug ("GoaKerberosIdentity: Destroying inactive credentials cache %s", name); - krb5_cc_destroy (self->kerberos_context, credentials_cache); - } - g_hash_table_remove_all (self->credentials_caches); - - return error_code == 0; - } - - GoaIdentity * - goa_kerberos_identity_new (krb5_context context, krb5_ccache cache, GError **error) - { - GoaKerberosIdentity *self; -- krb5_ccache copied_cache; - - self = GOA_KERBEROS_IDENTITY (g_object_new (GOA_TYPE_KERBEROS_IDENTITY, NULL)); - self->kerberos_context = context; - -- krb5_cc_dup (self->kerberos_context, cache, &copied_cache); -- goa_kerberos_identity_add_credentials_cache (self, copied_cache); -+ goa_kerberos_identity_add_credentials_cache (self, cache); - - error = NULL; - if (!g_initable_init (G_INITABLE (self), NULL, error)) - { - g_object_unref (self); - return NULL; - } - - return GOA_IDENTITY (self); - } -diff --git a/src/goaidentity/goakerberosidentity.h b/src/goaidentity/goakerberosidentity.h -index de0752cd..70cd4e3f 100644 ---- a/src/goaidentity/goakerberosidentity.h -+++ b/src/goaidentity/goakerberosidentity.h -@@ -14,54 +14,58 @@ - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, see . - */ - - #ifndef __GOA_KERBEROS_IDENTITY_H__ - #define __GOA_KERBEROS_IDENTITY_H__ - - #include - #include - - #include - #include "goaidentityinquiry.h" - - G_BEGIN_DECLS - - #define GOA_TYPE_KERBEROS_IDENTITY (goa_kerberos_identity_get_type ()) - G_DECLARE_FINAL_TYPE (GoaKerberosIdentity, goa_kerberos_identity, GOA, KERBEROS_IDENTITY, GObject); - - typedef enum - { - GOA_KERBEROS_IDENTITY_DESCRIPTION_REALM, - GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_AND_REALM, - GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_ROLE_AND_REALM - } GoaKerberosIdentityDescriptionLevel; - - GoaIdentity *goa_kerberos_identity_new (krb5_context kerberos_context, - krb5_ccache cache, - GError **error); - -+gboolean goa_kerberos_identity_has_credentials_cache (GoaKerberosIdentity *self, -+ krb5_ccache credentials_cache); -+void goa_kerberos_identity_add_credentials_cache (GoaKerberosIdentity *self, -+ krb5_ccache cache); -+ - gboolean goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, - const char *principal_name, - gconstpointer initial_password, - const char *preauth_source, - GoaIdentitySignInFlags flags, - GoaIdentityInquiryFunc inquiry_func, - gpointer inquiry_data, - GDestroyNotify destroy_notify, - GCancellable *cancellable, - GError **error); --void goa_kerberos_identity_update (GoaKerberosIdentity *identity, -- GoaKerberosIdentity *new_identity); -+void goa_kerberos_identity_refresh (GoaKerberosIdentity *identity); - gboolean goa_kerberos_identity_renew (GoaKerberosIdentity *self, - GError **error); - gboolean goa_kerberos_identity_erase (GoaKerberosIdentity *self, - GError **error); - - char *goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self); - char *goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self); - char *goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self); - - G_END_DECLS - - #endif /* __GOA_KERBEROS_IDENTITY_H__ */ -diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c -index 4fcb132c..f2d01571 100644 ---- a/src/goaidentity/goakerberosidentitymanager.c -+++ b/src/goaidentity/goakerberosidentitymanager.c -@@ -471,207 +471,227 @@ static void - drop_stale_identities (GoaKerberosIdentityManager *self, - Operation *operation, - GHashTable *known_identities) - { - GList *stale_identity_ids; - GList *node; - - stale_identity_ids = g_hash_table_get_keys (self->identities); - - node = stale_identity_ids; - while (node != NULL) - { - GoaIdentity *identity; - const char *identifier = node->data; - - identity = g_hash_table_lookup (known_identities, identifier); - if (identity == NULL) - { - identity = g_hash_table_lookup (self->identities, identifier); - - if (identity != NULL) - { - remove_identity (self, operation, identity); - } - } - node = node->next; - } - g_list_free (stale_identity_ids); - } - --static void --update_identity (GoaKerberosIdentityManager *self, -- Operation *operation, -- GoaIdentity *identity, -- GoaIdentity *new_identity) --{ -- -- goa_kerberos_identity_update (GOA_KERBEROS_IDENTITY (identity), -- GOA_KERBEROS_IDENTITY (new_identity)); -- -- if (goa_identity_is_signed_in (identity)) -- { -- IdentitySignalWork *work; -- -- /* if it's not expired, send out a refresh signal */ -- g_debug ("GoaKerberosIdentityManager: identity '%s' refreshed", -- goa_identity_get_identifier (identity)); -- -- work = identity_signal_work_new (self, identity); -- goa_kerberos_identify_manager_send_to_context (operation->context, -- (GSourceFunc) -- do_identity_signal_refreshed_work, -- work, -- (GDestroyNotify) -- identity_signal_work_free); -- } --} -- - static void - add_identity (GoaKerberosIdentityManager *self, - Operation *operation, - GoaIdentity *identity, - const char *identifier) - { - IdentitySignalWork *work; - - g_hash_table_replace (self->identities, g_strdup (identifier), g_object_ref (identity)); - - if (!goa_identity_is_signed_in (identity)) - g_hash_table_replace (self->expired_identities, g_strdup (identifier), identity); - - work = identity_signal_work_new (self, identity); - goa_kerberos_identify_manager_send_to_context (operation->context, - (GSourceFunc) - do_identity_signal_added_work, - work, - (GDestroyNotify) identity_signal_work_free); - } - -+static char * -+get_principal_from_cache (GoaKerberosIdentityManager *self, -+ krb5_ccache cache) -+{ -+ int error_code; -+ krb5_principal principal; -+ char *unparsed_name; -+ char *principal_name; -+ -+ error_code = krb5_cc_get_principal (self->kerberos_context, cache, &principal); -+ -+ error_code = krb5_unparse_name_flags (self->kerberos_context, principal, 0, &unparsed_name); -+ krb5_free_principal (self->kerberos_context, principal); -+ -+ if (error_code != 0) -+ return NULL; -+ -+ principal_name = g_strdup (unparsed_name); -+ -+ krb5_free_unparsed_name (self->kerberos_context, unparsed_name); -+ -+ return principal_name; -+} -+ - static void --refresh_identity (GoaKerberosIdentityManager *self, -- Operation *operation, -- GHashTable *refreshed_identities, -- GoaIdentity *identity) -+import_credentials_cache (GoaKerberosIdentityManager *self, -+ Operation *operation, -+ GHashTable *refreshed_identities, -+ krb5_ccache cache) - { -- const char *identifier; -- GoaIdentity *old_identity; -+ g_autofree char *identifier = NULL; -+ GoaIdentity *identity = NULL; - -- identifier = goa_identity_get_identifier (identity); -+ identifier = get_principal_from_cache (self, cache); - - if (identifier == NULL) - return; - -- old_identity = g_hash_table_lookup (self->identities, identifier); -+ identity = g_hash_table_lookup (self->identities, identifier); - -- if (old_identity != NULL) -+ if (identity == NULL) - { -- g_debug ("GoaKerberosIdentityManager: refreshing identity '%s'", identifier); -- update_identity (self, operation, old_identity, identity); -+ g_autoptr(GError) error = NULL; - -- /* Reuse the old identity, so any object data set up on it doesn't -- * disappear spurriously -- */ -- identifier = goa_identity_get_identifier (old_identity); -- identity = old_identity; -+ g_debug ("GoaKerberosIdentityManager: Adding new identity '%s'", identifier); -+ identity = goa_kerberos_identity_new (self->kerberos_context, cache, &error); -+ -+ if (error != NULL) -+ { -+ g_debug ("GoaKerberosIdentityManager: Could not track identity %s: %s", -+ identifier, error->message); -+ return; -+ } -+ -+ add_identity (self, operation, identity, identifier); - } - else - { -- g_debug ("GoaKerberosIdentityManager: adding new identity '%s'", identifier); -- add_identity (self, operation, identity, identifier); -+ if (!goa_kerberos_identity_has_credentials_cache (GOA_KERBEROS_IDENTITY (identity), cache)) -+ goa_kerberos_identity_add_credentials_cache (GOA_KERBEROS_IDENTITY (identity), cache); - } - -- /* Track refreshed identities so we can emit removals when we're done fully -+ /* Track refreshed identities so we can emit refreshes and removals when we're done fully - * enumerating the collection of credential caches - */ - g_hash_table_replace (refreshed_identities, - g_strdup (identifier), - g_object_ref (identity)); - } - - static gboolean - refresh_identities (GoaKerberosIdentityManager *self, - Operation *operation) - { - krb5_error_code error_code; - krb5_ccache cache; - krb5_cccol_cursor cursor; - const char *error_message; - GHashTable *refreshed_identities; -+ GHashTableIter iter; -+ const char *name; -+ GoaIdentity *identity; - - /* If we have more refreshes queued up, don't bother doing this one - */ - if (!g_atomic_int_dec_and_test (&self->pending_refresh_count)) - { - return FALSE; - } - - g_debug ("GoaKerberosIdentityManager: Refreshing identities"); - refreshed_identities = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - error_code = krb5_cccol_cursor_new (self->kerberos_context, &cursor); - - if (error_code != 0) - { - error_message = krb5_get_error_message (self->kerberos_context, error_code); - g_debug ("GoaKerberosIdentityManager: Error looking up available credential caches: %s", - error_message); - krb5_free_error_message (self->kerberos_context, error_message); - goto done; - } - - error_code = krb5_cccol_cursor_next (self->kerberos_context, cursor, &cache); - - while (error_code == 0 && cache != NULL) - { -- GoaIdentity *identity; -- -- identity = goa_kerberos_identity_new (self->kerberos_context, cache, NULL); -- -- if (identity != NULL) -- { -- refresh_identity (self, operation, refreshed_identities, identity); -- g_object_unref (identity); -- } -+ import_credentials_cache (self, operation, refreshed_identities, cache); - - krb5_cc_close (self->kerberos_context, cache); - error_code = krb5_cccol_cursor_next (self->kerberos_context, cursor, &cache); - } - - if (error_code != 0) - { - error_message = krb5_get_error_message (self->kerberos_context, error_code); - g_debug ("GoaKerberosIdentityManager: Error iterating over available credential caches: %s", - error_message); - krb5_free_error_message (self->kerberos_context, error_message); - } - - krb5_cccol_cursor_free (self->kerberos_context, &cursor); -+ -+ g_hash_table_iter_init (&iter, self->identities); -+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &identity)) -+ { -+ goa_kerberos_identity_refresh (GOA_KERBEROS_IDENTITY (identity)); -+ -+ if (goa_identity_is_signed_in (identity)) -+ { -+ IdentitySignalWork *work; -+ -+ /* if it's not expired, send out a refresh signal */ -+ g_debug ("GoaKerberosIdentityManager: identity '%s' refreshed", -+ goa_identity_get_identifier (identity)); -+ -+ work = identity_signal_work_new (self, identity); -+ goa_kerberos_identify_manager_send_to_context (operation->context, -+ (GSourceFunc) -+ do_identity_signal_refreshed_work, -+ work, -+ (GDestroyNotify) -+ identity_signal_work_free); -+ } -+ } -+ - done: - drop_stale_identities (self, operation, refreshed_identities); - g_hash_table_unref (refreshed_identities); - - return TRUE; - } - - static int - identity_sort_func (GoaIdentity *a, - GoaIdentity *b) - { - return g_strcmp0 (goa_identity_get_identifier (a), - goa_identity_get_identifier (b)); - } - - static void - free_identity_list (GList *list) - { - g_list_free_full (list, g_object_unref); - } - - static void - list_identities (GoaKerberosIdentityManager *self, - Operation *operation) - { - GList *identities; - - g_debug ("GoaKerberosIdentityManager: Listing identities"); - identities = g_hash_table_get_values (self->identities); - --- -2.39.0 - diff --git a/sources b/sources index 867da23..4307aab 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gnome-online-accounts-3.47.1.tar.xz) = 2050bd0d112f1c8b599e1d1d2e3e112a66624df96aa2ce5f2625206e4d98bddcacb160cb6ec07c63f57ca1435ba85b5b1ac4a4f80edc7ee5f40059cc1828bf06 +SHA512 (gnome-online-accounts-3.48.0.tar.xz) = 67e6c39d38187dc58175f6b694c66a92d9b9152b179db41ee05a8c512db5b16f1dd1eb1503c7d732b09126942abc00206ba956f7a54c6ae7a285a9e3101be7a5