diff --git a/SOURCES/fix-kerberos-crash.patch b/SOURCES/fix-kerberos-crash.patch deleted file mode 100644 index f2b7177..0000000 --- a/SOURCES/fix-kerberos-crash.patch +++ /dev/null @@ -1,144 +0,0 @@ -From 097aa719985923fd551c2c68a63ebca3609031ca Mon Sep 17 00:00:00 2001 -From: Ray Strode -Date: Wed, 6 Nov 2013 16:58:43 -0500 -Subject: [PATCH] kerberos: don't crash if keyring credentials disappear - -It's possible to make gnome-online-accounts crash by: - -1) creating a kerberos account in control-center -2) deleting the stored credentials from gnome keyring using seahorse -3) running kdestroy in the terminal to make the sign in button show up -in the control-center panel -4) clicking sign in - -This is because the provider makes the assumption that there will always -be stored credentials when signing in interactively, which is incorrect -in the above scenario (because of step 2). - -This commit hardens the get_ticket_sync function against credentials -disappearing. - -https://bugzilla.gnome.org/show_bug.cgi?id=711572 ---- - src/goabackend/goakerberosprovider.c | 24 ++++++++++++++---------- - 1 file changed, 14 insertions(+), 10 deletions(-) - -diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c -index e795856..fbe0364 100644 ---- a/src/goabackend/goakerberosprovider.c -+++ b/src/goabackend/goakerberosprovider.c -@@ -668,97 +668,101 @@ look_up_identity (GoaKerberosProvider *self, - operation_result); - } - - static void - on_account_signed_in (GoaProvider *provider, - GAsyncResult *result, - SignInRequest *request) - { - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), - &request->error)) - { - g_main_loop_quit (request->loop); - return; - } - - g_main_loop_quit (request->loop); - } - - static gboolean - get_ticket_sync (GoaKerberosProvider *self, - GoaObject *object, - gboolean is_interactive, - GCancellable *cancellable, - GError **error) - { - GVariant *credentials; - GError *lookup_error; - GoaAccount *account; - const char *identifier; - const char *password; -- gboolean has_password; - SignInRequest request; - gboolean ret; - - ret = FALSE; - - account = goa_object_peek_account (object); - identifier = goa_account_get_identity (account); - password = NULL; - - lookup_error = NULL; - credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (self), - object, - cancellable, - &lookup_error); - - if (credentials == NULL && !is_interactive) - { - if (lookup_error != NULL) - g_propagate_error (error, lookup_error); - else - g_set_error (error, - GOA_ERROR, - GOA_ERROR_NOT_AUTHORIZED, - _("Could not find saved credentials for principal `%s' in keyring"), identifier); - goto out; - } -- -- has_password = g_variant_lookup (credentials, "password", "&s", &password); -- if (!has_password && !is_interactive) -+ else if (credentials != NULL) - { -- g_set_error (error, -- GOA_ERROR, -- GOA_ERROR_NOT_AUTHORIZED, -- _("Did not find password for principal `%s' in credentials"), -- identifier); -- goto out; -+ gboolean has_password; -+ -+ has_password = g_variant_lookup (credentials, "password", "&s", &password); -+ -+ if (!has_password && !is_interactive) -+ { -+ g_set_error (error, -+ GOA_ERROR, -+ GOA_ERROR_NOT_AUTHORIZED, -+ _("Did not find password for principal `%s' in credentials"), -+ identifier); -+ goto out; -+ } - } - - memset (&request, 0, sizeof (SignInRequest)); - request.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); - request.error = NULL; - - sign_in_identity (self, - identifier, - password, - cancellable, - (GAsyncReadyCallback) - on_account_signed_in, - &request); - - g_main_loop_run (request.loop); - g_main_loop_unref (request.loop); - - if (request.error != NULL) - { - g_propagate_error (error, request.error); - goto out; - } - - ret = TRUE; - out: - if (credentials != NULL) - g_variant_unref (credentials); - - return ret; - } --- -1.8.3.1 - diff --git a/SOURCES/kerberos-fixes.patch b/SOURCES/kerberos-fixes.patch new file mode 100644 index 0000000..e73caed --- /dev/null +++ b/SOURCES/kerberos-fixes.patch @@ -0,0 +1,4663 @@ +From 0c9359604da3091c53a152cebe0e21861d2fceee Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:20:49 -0400 +Subject: [PATCH 01/24] kerberos: fix leak in register_error_domain + +In the event a type was not known, we were leaking the +type name. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goaidentityutils.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/goaidentity/goaidentityutils.c b/src/goaidentity/goaidentityutils.c +index 2730664..064c3f6 100644 +--- a/src/goaidentity/goaidentityutils.c ++++ b/src/goaidentity/goaidentityutils.c +@@ -152,67 +152,70 @@ dashed_string_to_dbus_error_string (const char *dashed_string, + g_strdup_printf ("%s.%s.%s", new_prefix, dashed_string, studly_suffix); + g_free (studly_suffix); + i += strlen (new_prefix) + 1; + + dbus_error_string_length = strlen (dbus_error_string); + + dbus_error_string[i] = g_ascii_toupper (dbus_error_string[i]); + i++; + + while (i < dbus_error_string_length) + { + if (dbus_error_string[i] == '_' || dbus_error_string[i] == '-') + { + dbus_error_string[i] = '.'; + + if (g_ascii_isalpha (dbus_error_string[i + 1])) + dbus_error_string[i + 1] = g_ascii_toupper (dbus_error_string[i + 1]); + } + + i++; + } + + return dbus_error_string; + } + + void + goa_identity_utils_register_error_domain (GQuark error_domain, + GType error_enum) + { + const char *error_domain_string; +- char *type_name; ++ char *type_name = NULL; + GType type; + GTypeClass *type_class; + GEnumClass *enum_class; + guint i; + + error_domain_string = g_quark_to_string (error_domain); + type_name = dashed_string_to_studly_caps (error_domain_string); + type = g_type_from_name (type_name); + type_class = g_type_class_ref (type); + + if (type_class == NULL) + { +- goa_warning ("GoaIdentityUtils: Could not identity type %s", type_name); +- return; ++ g_warning ("GoaIdentityUtils: Could not identity type %s", type_name); ++ goto out; + } + + enum_class = G_ENUM_CLASS (type_class); + + for (i = 0; i < enum_class->n_values; i++) + { + char *dbus_error_string; + + dbus_error_string = dashed_string_to_dbus_error_string (error_domain_string, + "goa", + "org.gnome", + enum_class->values[i]. + value_nick); + + goa_debug ("GoaIdentityUtils: Registering dbus error %s", dbus_error_string); + g_dbus_error_register_error (error_domain, + enum_class->values[i].value, dbus_error_string); + g_free (dbus_error_string); + } + + g_type_class_unref (type_class); ++ ++ out: ++ g_free (type_name); + } +-- +1.8.3.1 + + +From 74834f7fa69b8f5ad66008ad93029f8100fd28ee Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:26:03 -0400 +Subject: [PATCH 02/24] kerberos: refactor register_error_domain + +This commit move type class freeing to the +otherside of the "out" label to make the code +robust against potential future changes. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goaidentityutils.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/goaidentity/goaidentityutils.c b/src/goaidentity/goaidentityutils.c +index 064c3f6..9206099 100644 +--- a/src/goaidentity/goaidentityutils.c ++++ b/src/goaidentity/goaidentityutils.c +@@ -154,68 +154,67 @@ dashed_string_to_dbus_error_string (const char *dashed_string, + i += strlen (new_prefix) + 1; + + dbus_error_string_length = strlen (dbus_error_string); + + dbus_error_string[i] = g_ascii_toupper (dbus_error_string[i]); + i++; + + while (i < dbus_error_string_length) + { + if (dbus_error_string[i] == '_' || dbus_error_string[i] == '-') + { + dbus_error_string[i] = '.'; + + if (g_ascii_isalpha (dbus_error_string[i + 1])) + dbus_error_string[i + 1] = g_ascii_toupper (dbus_error_string[i + 1]); + } + + i++; + } + + return dbus_error_string; + } + + void + goa_identity_utils_register_error_domain (GQuark error_domain, + GType error_enum) + { + const char *error_domain_string; + char *type_name = NULL; + GType type; +- GTypeClass *type_class; ++ GTypeClass *type_class = NULL; + GEnumClass *enum_class; + guint i; + + error_domain_string = g_quark_to_string (error_domain); + type_name = dashed_string_to_studly_caps (error_domain_string); + type = g_type_from_name (type_name); + type_class = g_type_class_ref (type); + + if (type_class == NULL) + { + g_warning ("GoaIdentityUtils: Could not identity type %s", type_name); + goto out; + } + + enum_class = G_ENUM_CLASS (type_class); + + for (i = 0; i < enum_class->n_values; i++) + { + char *dbus_error_string; + + dbus_error_string = dashed_string_to_dbus_error_string (error_domain_string, + "goa", + "org.gnome", + enum_class->values[i]. + value_nick); + + goa_debug ("GoaIdentityUtils: Registering dbus error %s", dbus_error_string); + g_dbus_error_register_error (error_domain, + enum_class->values[i].value, dbus_error_string); + g_free (dbus_error_string); + } + +- g_type_class_unref (type_class); +- + out: ++ g_clear_pointer (&type_class, g_type_class_unref); + g_free (type_name); + } +-- +1.8.3.1 + + +From 8cc66523fe300f96cd4b34e4ad16c99e9ed86731 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 17 Mar 2014 09:59:12 -0400 +Subject: [PATCH 03/24] kerberos: make enum_class in register_error_domain + const + +Just as a defensive programming measure. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goaidentityutils.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/goaidentity/goaidentityutils.c b/src/goaidentity/goaidentityutils.c +index 9206099..abc8e2d 100644 +--- a/src/goaidentity/goaidentityutils.c ++++ b/src/goaidentity/goaidentityutils.c +@@ -155,61 +155,61 @@ dashed_string_to_dbus_error_string (const char *dashed_string, + + dbus_error_string_length = strlen (dbus_error_string); + + dbus_error_string[i] = g_ascii_toupper (dbus_error_string[i]); + i++; + + while (i < dbus_error_string_length) + { + if (dbus_error_string[i] == '_' || dbus_error_string[i] == '-') + { + dbus_error_string[i] = '.'; + + if (g_ascii_isalpha (dbus_error_string[i + 1])) + dbus_error_string[i + 1] = g_ascii_toupper (dbus_error_string[i + 1]); + } + + i++; + } + + return dbus_error_string; + } + + void + goa_identity_utils_register_error_domain (GQuark error_domain, + GType error_enum) + { + const char *error_domain_string; + char *type_name = NULL; + GType type; + GTypeClass *type_class = NULL; +- GEnumClass *enum_class; ++ const GEnumClass *enum_class; + guint i; + + error_domain_string = g_quark_to_string (error_domain); + type_name = dashed_string_to_studly_caps (error_domain_string); + type = g_type_from_name (type_name); + type_class = g_type_class_ref (type); + + if (type_class == NULL) + { + g_warning ("GoaIdentityUtils: Could not identity type %s", type_name); + goto out; + } + + enum_class = G_ENUM_CLASS (type_class); + + for (i = 0; i < enum_class->n_values; i++) + { + char *dbus_error_string; + + dbus_error_string = dashed_string_to_dbus_error_string (error_domain_string, + "goa", + "org.gnome", + enum_class->values[i]. + value_nick); + + goa_debug ("GoaIdentityUtils: Registering dbus error %s", dbus_error_string); + g_dbus_error_register_error (error_domain, + enum_class->values[i].value, dbus_error_string); + g_free (dbus_error_string); + } +-- +1.8.3.1 + + +From e63005c61f4307a279863bde168b793d2840c575 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:28:32 -0400 +Subject: [PATCH 04/24] kerberos: fix leak in add_temporary_account + +The account description was not getting freed. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goaidentityservice.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c +index 1639868..83b40af 100644 +--- a/src/goaidentity/goaidentityservice.c ++++ b/src/goaidentity/goaidentityservice.c +@@ -850,116 +850,117 @@ on_account_added (GoaManager *manager, + } + + if (object_path != NULL && object_path[0] != '\0') + { + goa_debug ("Created account for identity with object path %s", object_path); + + object_manager = goa_client_get_object_manager (self->priv->client); + object = GOA_OBJECT (g_dbus_object_manager_get_object (object_manager, + object_path)); + g_free (object_path); + } + + if (object == NULL) + g_simple_async_result_set_op_res_gpointer (operation_result, NULL, NULL); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + object, + (GDestroyNotify) + g_object_unref); + + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); + } + + static void + add_temporary_account (GoaIdentityService *self, + GoaIdentity *identity) + { + char *realm; + const char *principal; +- const char *principal_for_display; ++ char *principal_for_display; + GSimpleAsyncResult *operation_result; + GVariantBuilder credentials; + GVariantBuilder details; + GoaObject *object; + + principal = goa_identity_get_identifier (identity); + + object = g_hash_table_lookup (self->priv->pending_temporary_account_results, + principal); + + if (object != NULL) + { + goa_debug ("GoaIdentityService: would add temporary identity %s, but it's already pending", principal); + return; + } + + goa_debug ("GoaIdentityService: adding temporary identity %s", principal); + + /* If there's no account for this identity then create a temporary one. + */ + principal_for_display = goa_identity_manager_name_identity (self->priv->identity_manager, + identity); + + realm = goa_kerberos_identity_get_realm_name (GOA_KERBEROS_IDENTITY (identity)); + + g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&details, "{ss}", "Realm", realm); + g_variant_builder_add (&details, "{ss}", "IsTemporary", "true"); + g_variant_builder_add (&details, "{ss}", "TicketingEnabled", "true"); + + + goa_debug ("GoaIdentityService: asking to sign back in"); + + operation_result = g_simple_async_result_new (G_OBJECT (self), + (GAsyncReadyCallback) + on_temporary_account_created_for_identity, + identity, + add_temporary_account); + g_hash_table_insert (self->priv->pending_temporary_account_results, + g_strdup (principal), + g_object_ref (operation_result)); + + goa_manager_call_add_account (self->priv->accounts_manager, + "kerberos", + principal, + principal_for_display, + g_variant_builder_end (&credentials), + g_variant_builder_end (&details), + NULL, + (GAsyncReadyCallback) + on_account_added, + operation_result); + g_free (realm); ++ g_free (principal_for_display); + } + + 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) + add_temporary_account (self, identity); + } + + 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); +-- +1.8.3.1 + + +From f9f597f8df9b00bd2e42ce6177fa4505d2e8c3d9 Mon Sep 17 00:00:00 2001 +From: Michael Cronenworth +Date: Mon, 17 Mar 2014 09:33:59 -0400 +Subject: [PATCH 05/24] kerberos: don't free alarm in set_alarm, free in + callers + +It's a little unexpected that set_alarm "eats" the alarm passed in. +This commit makes it the caller's responsibility to free the alarm +to more closely match typical practice. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goakerberosidentity.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index 048f36b..2c1cdf7 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -760,61 +760,60 @@ on_expiring_alarm_fired (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + + g_clear_object (&self->priv->expiring_alarm_cancellable); + + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) + { + goa_debug ("GoaKerberosIdentity: expiring alarm fired for signed-in identity"); + g_signal_emit (G_OBJECT (self), signals[EXPIRING], 0); + } + } + + static void + set_alarm (GoaKerberosIdentity *self, + GoaAlarm *alarm, + GDateTime *alarm_time, + GCancellable **cancellable) + { + GDateTime *old_alarm_time; + + G_LOCK (identity_lock); + old_alarm_time = goa_alarm_get_time (alarm); + if (old_alarm_time == NULL || !g_date_time_equal (alarm_time, old_alarm_time)) + { + GCancellable *new_cancellable; + + new_cancellable = g_cancellable_new (); + goa_alarm_set_time (alarm, alarm_time, new_cancellable); +- g_date_time_unref (alarm_time); + + g_clear_object (cancellable); + *cancellable = new_cancellable; + } + G_UNLOCK (identity_lock); + + } + + static void + disconnect_alarm_signals (GoaKerberosIdentity *self) + { + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), + G_CALLBACK (on_renewal_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), + G_CALLBACK (on_renewal_alarm_rearmed), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), + G_CALLBACK (on_expiring_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), + G_CALLBACK (on_expiration_alarm_rearmed), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), + G_CALLBACK (on_expiration_alarm_fired), + self); + g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), + G_CALLBACK (on_expiring_alarm_rearmed), + self); + } +@@ -855,60 +854,63 @@ reset_alarms (GoaKerberosIdentity *self) + GDateTime *expiration_time; + GDateTime *expiring_time; + GDateTime *renewal_time; + GTimeSpan time_span_until_expiration; + + now = g_date_time_new_now_local (); + expiration_time = g_date_time_new_from_unix_local (self->priv->expiration_time); + time_span_until_expiration = g_date_time_difference (expiration_time, now); + + /* Let the user reauthenticate 10 min before expiration */ + expiring_time = g_date_time_add_minutes (expiration_time, -10); + + /* Try to quietly auto-renew halfway through so in ideal configurations + * the ticket is never more than halfway to expired + */ + renewal_time = g_date_time_add (expiration_time, + -(time_span_until_expiration / 2)); + + disconnect_alarm_signals (self); + + set_alarm (self, + self->priv->renewal_alarm, + renewal_time, &self->priv->renewal_alarm_cancellable); + set_alarm (self, + self->priv->expiring_alarm, + expiring_time, &self->priv->expiring_alarm_cancellable); + set_alarm (self, + self->priv->expiration_alarm, + expiration_time, &self->priv->expiration_alarm_cancellable); + ++ g_date_time_unref (renewal_time); ++ g_date_time_unref (expiring_time); ++ g_date_time_unref (expiration_time); + connect_alarm_signals (self); + } + + static void + cancel_and_clear_cancellable (GCancellable **cancellable) + { + if (cancellable == NULL) + return; + + if (!g_cancellable_is_cancelled (*cancellable)) + g_cancellable_cancel (*cancellable); + + g_clear_object (cancellable); + } + + static void + clear_alarms (GoaKerberosIdentity *self) + { + cancel_and_clear_cancellable (&self->priv->renewal_alarm_cancellable); + cancel_and_clear_cancellable (&self->priv->expiring_alarm_cancellable); + cancel_and_clear_cancellable (&self->priv->expiration_alarm_cancellable); + } + + static gboolean + goa_kerberos_identity_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) + { + GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable); + GError *verification_error; +-- +1.8.3.1 + + +From ef2b475703f482759d3b8af03d72ef8f04a53c84 Mon Sep 17 00:00:00 2001 +From: Michael Cronenworth +Date: Mon, 17 Mar 2014 09:33:59 -0400 +Subject: [PATCH 06/24] kerberos: fix leak in reset_alarms + +A GDateTime "now" object wasn't getting cleaned up after use. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goakerberosidentity.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index 2c1cdf7..ddbad75 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -832,60 +832,61 @@ connect_alarm_signals (GoaKerberosIdentity *self) + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "fired", + G_CALLBACK (on_expiring_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "rearmed", + G_CALLBACK (on_expiring_alarm_rearmed), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "fired", + G_CALLBACK (on_expiration_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "rearmed", + G_CALLBACK (on_expiration_alarm_rearmed), + self); + } + + static void + reset_alarms (GoaKerberosIdentity *self) + { + GDateTime *now; + GDateTime *expiration_time; + GDateTime *expiring_time; + GDateTime *renewal_time; + GTimeSpan time_span_until_expiration; + + now = g_date_time_new_now_local (); + expiration_time = g_date_time_new_from_unix_local (self->priv->expiration_time); + time_span_until_expiration = g_date_time_difference (expiration_time, now); ++ g_date_time_unref (now); + + /* Let the user reauthenticate 10 min before expiration */ + expiring_time = g_date_time_add_minutes (expiration_time, -10); + + /* Try to quietly auto-renew halfway through so in ideal configurations + * the ticket is never more than halfway to expired + */ + renewal_time = g_date_time_add (expiration_time, + -(time_span_until_expiration / 2)); + + disconnect_alarm_signals (self); + + set_alarm (self, + self->priv->renewal_alarm, + renewal_time, &self->priv->renewal_alarm_cancellable); + set_alarm (self, + self->priv->expiring_alarm, + expiring_time, &self->priv->expiring_alarm_cancellable); + set_alarm (self, + self->priv->expiration_alarm, + expiration_time, &self->priv->expiration_alarm_cancellable); + + g_date_time_unref (renewal_time); + g_date_time_unref (expiring_time); + g_date_time_unref (expiration_time); + connect_alarm_signals (self); + } + + static void + cancel_and_clear_cancellable (GCancellable **cancellable) +-- +1.8.3.1 + + +From 226db9d5df98b9872b9930b0899d923bb4418f70 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:42:13 -0400 +Subject: [PATCH 07/24] kerberos: fix principal leak in identity_renew + +The code carefully freed the principal in all error cases, but then +failed to free the principal in the non-error case! + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goakerberosidentity.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index ddbad75..d8c5c66 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -1379,60 +1379,63 @@ goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) + name = goa_kerberos_identity_get_principal_name (self); + + error_code = krb5_get_renewed_creds (self->priv->kerberos_context, + &new_credentials, + principal, + self->priv->credentials_cache, NULL); + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_RENEWING, + error_code, + _ + ("Could not get new credentials to renew identity %s: %k"), + name); + krb5_free_principal (self->priv->kerberos_context, principal); + goto out; + } + + if (!goa_kerberos_identity_update_credentials (self, + principal, + &new_credentials, + error)) + { + krb5_free_principal (self->priv->kerberos_context, principal); + goto out; + } + + goa_debug ("GoaKerberosIdentity: identity %s renewed", name); + renewed = TRUE; ++ ++ krb5_free_principal (self->priv->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->priv->credentials_cache != NULL) + { + error_code = krb5_cc_destroy (self->priv->kerberos_context, + self->priv->credentials_cache); + self->priv->credentials_cache = NULL; + } + + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, + error_code, _("Could not erase identity: %k")); + return FALSE; + } + + return TRUE; + } + +-- +1.8.3.1 + + +From 30d997961fe88b02a9405219563c5d7df13f99ab Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:44:05 -0400 +Subject: [PATCH 08/24] kerberos: consolidate exit path code in identity_renew + +the principal is freed in 3 different places. This commit +consolidates it to one place for clarity and added robustness +against future changes. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + 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 d8c5c66..200c74b 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -1364,76 +1364,75 @@ goa_kerberos_identity_renew (GoaKerberosIdentity *self, GError **error) + goto out; + } + + error_code = krb5_cc_get_principal (self->priv->kerberos_context, + self->priv->credentials_cache, &principal); + + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, + error_code, _("Could not renew identity: %k")); + goto out; + } + + name = goa_kerberos_identity_get_principal_name (self); + + error_code = krb5_get_renewed_creds (self->priv->kerberos_context, + &new_credentials, + principal, + self->priv->credentials_cache, NULL); + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_RENEWING, + error_code, + _ + ("Could not get new credentials to renew identity %s: %k"), + name); +- krb5_free_principal (self->priv->kerberos_context, principal); +- goto out; ++ goto free_principal; + } + + if (!goa_kerberos_identity_update_credentials (self, + principal, + &new_credentials, + error)) + { +- krb5_free_principal (self->priv->kerberos_context, principal); +- goto out; ++ goto free_principal; + } + + goa_debug ("GoaKerberosIdentity: identity %s renewed", name); + renewed = TRUE; + ++free_principal: + krb5_free_principal (self->priv->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->priv->credentials_cache != NULL) + { + error_code = krb5_cc_destroy (self->priv->kerberos_context, + self->priv->credentials_cache); + self->priv->credentials_cache = NULL; + } + + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_REMOVING_CREDENTIALS, + error_code, _("Could not erase identity: %k")); + return FALSE; + } + + return TRUE; +-- +1.8.3.1 + + +From 2a599702c690712a0b1fd6b04d59b8d5b21a8548 Mon Sep 17 00:00:00 2001 +From: Michael Cronenworth +Date: Mon, 17 Mar 2014 09:47:01 -0400 +Subject: [PATCH 09/24] kerberos: fix leak in verify_identity + +The kerberos API allocates credentials on the heap, when iterating +over a credential collection (for each iteration). + +This commit makes sure each of those credentials is freed. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goakerberosidentity.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index 200c74b..15a719d 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -608,60 +608,62 @@ verify_identity (GoaKerberosIdentity *self, + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, + error_code, + _("Could not find identity " + "credentials in cache: %k")); + + verification_level = VERIFICATION_LEVEL_ERROR; + goto out; + } + + verification_level = VERIFICATION_LEVEL_UNVERIFIED; + + error_code = krb5_cc_next_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor, + &credentials); + + while (error_code == 0) + { + if (credentials_validate_existence (self, principal, &credentials)) + { + if (!credentials_are_expired (self, &credentials)) + verification_level = VERIFICATION_LEVEL_SIGNED_IN; + else + verification_level = VERIFICATION_LEVEL_EXISTS; + } + ++ krb5_free_cred_contents (self->priv->kerberos_context, &credentials); ++ + error_code = krb5_cc_next_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor, + &credentials); + } + + if (error_code != KRB5_CC_END) + { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, + error_code, + _("Could not sift through identity " + "credentials in cache: %k")); + goto out; + } + + error_code = krb5_cc_end_seq_get (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor); + + if (error_code != 0) + { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, +-- +1.8.3.1 + + +From 4eeea6fa96c27148d47dd5d70c7448c551049aa8 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 17 Mar 2014 09:51:27 -0400 +Subject: [PATCH 10/24] kerberos: make sure credential cache sequence is always + explicitly ended + +The code currently fails to pair krb5_cc_start_seq_get with krb5_cc_end_seq_get +in an error case. This can lead the cursor getting leaked. + +https://bugzilla.gnome.org/show_bug.cgi?id=726353 +--- + src/goaidentity/goakerberosidentity.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index 15a719d..9308295 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -626,63 +626,64 @@ verify_identity (GoaKerberosIdentity *self, + &credentials); + + while (error_code == 0) + { + if (credentials_validate_existence (self, principal, &credentials)) + { + if (!credentials_are_expired (self, &credentials)) + verification_level = VERIFICATION_LEVEL_SIGNED_IN; + else + verification_level = VERIFICATION_LEVEL_EXISTS; + } + + krb5_free_cred_contents (self->priv->kerberos_context, &credentials); + + error_code = krb5_cc_next_cred (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor, + &credentials); + } + + if (error_code != KRB5_CC_END) + { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, + error_code, + _("Could not sift through identity " + "credentials in cache: %k")); +- goto out; ++ goto end_sequence; + } + ++ end_sequence: + error_code = krb5_cc_end_seq_get (self->priv->kerberos_context, + self->priv->credentials_cache, + &cursor); + + if (error_code != 0) + { + verification_level = VERIFICATION_LEVEL_ERROR; + + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, + error_code, + _("Could not finish up sifting through " + "identity credentials in cache: %k")); + goto out; + } + out: + krb5_free_principal (self->priv->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->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) + is_signed_in = TRUE; +-- +1.8.3.1 + + +From cb0607ad1aa5eedeefa410a1297e240b43d1eaaa Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Wed, 9 Apr 2014 14:44:46 +0200 +Subject: [PATCH 11/24] identity: Remove unused variables + +Fallout from f4c3d303bf7ecc9e0cc6288d787c118365c1d34a + +Fixes: https://bugzilla.gnome.org/686416 +--- + src/goaidentity/goaidentityservice.c | 9 --------- + 1 file changed, 9 deletions(-) + +diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c +index 83b40af..3321b0f 100644 +--- a/src/goaidentity/goaidentityservice.c ++++ b/src/goaidentity/goaidentityservice.c +@@ -21,63 +21,60 @@ + */ + + #include "config.h" + #include "goaidentityservice.h" + + #include + #include + + #include + #include + #include + + #include + + #include + + #include "goaidentityenumtypes.h" + #include "goaidentityutils.h" + + #include "goakerberosidentitymanager.h" + #include "goalogging.h" + + struct _GoaIdentityServicePrivate + { + GDBusConnection *connection; + GDBusObjectManagerServer *object_manager_server; + guint bus_id; + + GoaIdentityManager *identity_manager; + +- guint realmd_watch; +- GCancellable *cancellable; +- + GHashTable *watched_client_connections; + GHashTable *key_holders; + GHashTable *pending_temporary_account_results; + + /* FIXME: This is a little ucky, since the goa service + * is in process, we should able to use direct calls. + */ + GoaClient *client; + GoaManager *accounts_manager; + }; + + static void identity_service_manager_interface_init (GoaIdentityServiceManagerIface *interface); + + static void + sign_in (GoaIdentityService *self, + const char *identifier, + gconstpointer initial_password, + GoaIdentitySignInFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + + G_DEFINE_TYPE_WITH_CODE (GoaIdentityService, + goa_identity_service, + GOA_IDENTITY_SERVICE_TYPE_MANAGER_SKELETON, + G_IMPLEMENT_INTERFACE (GOA_IDENTITY_SERVICE_TYPE_MANAGER, + identity_service_manager_interface_init)); + + static char * + get_object_path_for_identity (GoaIdentityService *self, +@@ -1727,101 +1724,95 @@ on_session_bus_acquired (GDBusConnection *connection, + } + + static void + on_name_acquired (GDBusConnection *connection, + const char *name, + GoaIdentityService *self) + { + if (g_strcmp0 (name, "org.gnome.Identity") == 0) + goa_debug ("GoaIdentityService: Acquired name org.gnome.Identity"); + } + + static void + on_name_lost (GDBusConnection *connection, + const char *name, + GoaIdentityService *self) + { + if (g_strcmp0 (name, "org.gnome.Identity") == 0) + goa_debug ("GoaIdentityService: Lost name org.gnome.Identity"); + } + + gboolean + goa_identity_service_activate (GoaIdentityService *self, + GError **error) + { + GoaIdentityServiceObjectSkeleton *object; + + g_return_val_if_fail (GOA_IS_IDENTITY_SERVICE (self), FALSE); + + goa_debug ("GoaIdentityService: Activating identity service"); + +- self->priv->cancellable = g_cancellable_new (); +- + self->priv->object_manager_server = + g_dbus_object_manager_server_new ("/org/gnome/Identity"); + + object = goa_identity_service_object_skeleton_new ("/org/gnome/Identity/Manager"); + goa_identity_service_object_skeleton_set_manager (object, + GOA_IDENTITY_SERVICE_MANAGER (self)); + + g_dbus_object_manager_server_export (self->priv->object_manager_server, + G_DBUS_OBJECT_SKELETON (object)); + g_object_unref (object); + + self->priv->bus_id = g_bus_own_name (G_BUS_TYPE_SESSION, + "org.gnome.Identity", + G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | + G_BUS_NAME_OWNER_FLAGS_REPLACE, + (GBusAcquiredCallback) on_session_bus_acquired, + (GBusNameAcquiredCallback) on_name_acquired, + (GBusNameVanishedCallback) on_name_lost, + self, + NULL); + + return TRUE; + } + + void + goa_identity_service_deactivate (GoaIdentityService *self) + { + goa_debug ("GoaIdentityService: Deactivating identity service"); + +- if (self->priv->realmd_watch == 0) +- g_bus_unwatch_name (self->priv->realmd_watch); +- + if (self->priv->identity_manager != NULL) + { + g_signal_handlers_disconnect_by_func (self, on_identity_needs_renewal, self); + g_signal_handlers_disconnect_by_func (self, on_identity_expiring, self); + g_signal_handlers_disconnect_by_func (self, on_identity_expired, self); + g_clear_object (&self->priv->identity_manager); + } + + g_clear_object (&self->priv->object_manager_server); + g_clear_object (&self->priv->connection); + g_clear_object (&self->priv->client); +- g_clear_object (&self->priv->cancellable); + } + + static void + goa_identity_service_class_init (GoaIdentityServiceClass *service_class) + { + GObjectClass *object_class = G_OBJECT_CLASS (service_class); + + object_class->finalize = goa_identity_service_finalize; + + goa_identity_utils_register_error_domain (GOA_IDENTITY_ERROR, GOA_TYPE_IDENTITY_ERROR); + goa_identity_utils_register_error_domain (GOA_IDENTITY_MANAGER_ERROR, GOA_TYPE_IDENTITY_MANAGER_ERROR); + + g_type_class_add_private (service_class, sizeof (GoaIdentityServicePrivate)); + } + + GoaIdentityService * + goa_identity_service_new (void) + { + GObject *object; + + object = g_object_new (GOA_TYPE_IDENTITY_SERVICE, + NULL); + + return GOA_IDENTITY_SERVICE (object); + } +-- +1.8.3.1 + + +From 068c67b1d75dbda1b75da4c3f4d1e330eb47e130 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Wed, 9 Apr 2014 15:25:33 +0200 +Subject: [PATCH 12/24] kerberos: Correctly set the error if object creation + failed + +The error is actually a GError **, and not a GError *. There is no need +to set it to NULL. + +Fixes: https://bugzilla.gnome.org/727896 +--- + src/goaidentity/goakerberosidentitymanager.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c +index f3664fe..5e33003 100644 +--- a/src/goaidentity/goakerberosidentitymanager.c ++++ b/src/goaidentity/goakerberosidentitymanager.c +@@ -1645,47 +1645,46 @@ goa_kerberos_identity_manager_finalize (GObject *object) + krb5_free_context (self->priv->kerberos_context); + + G_OBJECT_CLASS (goa_kerberos_identity_manager_parent_class)->finalize (object); + } + + static void + goa_kerberos_identity_manager_class_init (GoaKerberosIdentityManagerClass *klass) + { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = goa_kerberos_identity_manager_dispose; + object_class->finalize = goa_kerberos_identity_manager_finalize; + + g_type_class_add_private (klass, sizeof (GoaKerberosIdentityManagerPrivate)); + } + + GoaIdentityManager * + goa_kerberos_identity_manager_new (GCancellable * cancellable, GError ** error) + { + if (goa_kerberos_identity_manager_singleton == NULL) + { + GObject *object; + + object = g_object_new (GOA_TYPE_KERBEROS_IDENTITY_MANAGER, NULL); + + goa_kerberos_identity_manager_singleton = GOA_IDENTITY_MANAGER (object); + g_object_add_weak_pointer (object, + (gpointer *) & + goa_kerberos_identity_manager_singleton); + +- error = NULL; + if (!g_initable_init (G_INITABLE (object), cancellable, error)) + { + g_object_unref (object); + return NULL; + } + + } + else + { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + g_object_ref (goa_kerberos_identity_manager_singleton); + } + + return goa_kerberos_identity_manager_singleton; + } +-- +1.8.3.1 + + +From 013eec484076d88d818030d88cea485171981e89 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Wed, 9 Apr 2014 15:31:50 +0200 +Subject: [PATCH 13/24] identity: Keep a reference to self during async + operations + +If the IdentityService object is freed while it's getting the initial +list of identities shortly after start up (i.e., the service is +exiting shortly after starting), then there's a chance the callback +associated with the identity listing operation could be called after +the service is freed (which could lead to a crash). + +This commit addresses that problem by reffing the service object until +the list operation finishes, preventing it from getting freed +prematurely. + +Fixes: https://bugzilla.gnome.org/727896 +--- + src/goaidentity/goaidentityservice.c | 20 +++++++++++++------- + 1 file changed, 13 insertions(+), 7 deletions(-) + +diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c +index 3321b0f..d51d6bd 100644 +--- a/src/goaidentity/goaidentityservice.c ++++ b/src/goaidentity/goaidentityservice.c +@@ -1571,80 +1571,83 @@ on_identities_listed (GoaIdentityManager *manager, + "identity-refreshed", + G_CALLBACK (on_identity_refreshed), + self); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-needs-renewal", + G_CALLBACK (on_identity_needs_renewal), + self); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-expiring", + G_CALLBACK (on_identity_expiring), + self); + g_signal_connect (G_OBJECT (self->priv->identity_manager), + "identity-expired", + G_CALLBACK (on_identity_expired), + self); + + g_signal_connect (G_OBJECT (self->priv->client), + "account-removed", + G_CALLBACK (on_account_removed), + self); + + identities = goa_identity_manager_list_identities_finish (manager, result, &error); + + if (identities == NULL) + { + if (error != NULL) + { + goa_warning ("Could not list identities: %s", error->message); + g_error_free (error); + } +- return; ++ goto out; + } + + for (node = identities; node != NULL; node = node->next) + { + GoaIdentity *identity = node->data; + const char *principal; + GoaObject *object; + + export_identity (self, identity); + + principal = goa_identity_get_identifier (identity); + + object = find_object_with_principal (self, principal, TRUE); + + if (object == NULL) + add_temporary_account (self, identity); + else + g_object_unref (object); + } ++ ++ out: ++ g_object_unref (self); + } + + static void + ensure_credentials_for_accounts (GoaIdentityService *self) + { + GDBusObjectManager *object_manager; + GList *accounts; + GList *node; + + object_manager = goa_client_get_object_manager (self->priv->client); + + g_signal_connect (G_OBJECT (object_manager), + "interface-added", + G_CALLBACK (on_account_interface_added), + self); + g_signal_connect (G_OBJECT (object_manager), + "interface-removed", + G_CALLBACK (on_account_interface_removed), + self); + + accounts = goa_client_get_accounts (self->priv->client); + + for (node = accounts; node != NULL; node = node->next) + { + GoaObject *object = GOA_OBJECT (node->data); + GoaAccount *account; + GoaTicketing *ticketing; + const char *provider_type; + + account = goa_object_peek_account (object); +@@ -1652,101 +1655,104 @@ ensure_credentials_for_accounts (GoaIdentityService *self) + if (account == NULL) + continue; + + provider_type = goa_account_get_provider_type (account); + + if (g_strcmp0 (provider_type, "kerberos") != 0) + continue; + + ticketing = goa_object_peek_ticketing (object); + + if (ticketing == NULL) + continue; + + ensure_account_credentials (self, object); + } + } + + static void + on_got_client (GoaClient *client, + GAsyncResult *result, + GoaIdentityService *self) + { + GError *error; + + error = NULL; + + self->priv->client = goa_client_new_finish (result, &error); + + if (self->priv->client == NULL) + { +- goa_warning ("Could not create client: %s", error->message); +- return; ++ g_warning ("Could not create client: %s", error->message); ++ goto out; + } + + self->priv->accounts_manager = goa_client_get_manager (client); + + self->priv->identity_manager = goa_kerberos_identity_manager_new (NULL, &error); + + if (self->priv->identity_manager == NULL) + { +- goa_warning ("Could not create identity manager: %s", error->message); +- return; ++ g_warning ("Could not create identity manager: %s", error->message); ++ goto out; + } + + goa_identity_manager_list_identities (self->priv->identity_manager, + NULL, + (GAsyncReadyCallback) + on_identities_listed, +- self); ++ g_object_ref (self)); + + ensure_credentials_for_accounts (self); ++ ++ out: ++ g_object_unref (self); + } + + static void + on_session_bus_acquired (GDBusConnection *connection, + const char *unique_name, + GoaIdentityService *self) + { + goa_debug ("GoaIdentityService: Connected to session bus"); + + if (self->priv->connection == NULL) + { + self->priv->connection = g_object_ref (connection); + + g_dbus_object_manager_server_set_connection (self->priv->object_manager_server, + self->priv->connection); + + goa_client_new (NULL, + (GAsyncReadyCallback) + on_got_client, +- self); ++ g_object_ref (self)); + } + } + + static void + on_name_acquired (GDBusConnection *connection, + const char *name, + GoaIdentityService *self) + { + if (g_strcmp0 (name, "org.gnome.Identity") == 0) + goa_debug ("GoaIdentityService: Acquired name org.gnome.Identity"); + } + + static void + on_name_lost (GDBusConnection *connection, + const char *name, + GoaIdentityService *self) + { + if (g_strcmp0 (name, "org.gnome.Identity") == 0) + goa_debug ("GoaIdentityService: Lost name org.gnome.Identity"); + } + + gboolean + goa_identity_service_activate (GoaIdentityService *self, + GError **error) + { + GoaIdentityServiceObjectSkeleton *object; + + g_return_val_if_fail (GOA_IS_IDENTITY_SERVICE (self), FALSE); + + goa_debug ("GoaIdentityService: Activating identity service"); +-- +1.8.3.1 + + +From 24f2a2a6ded35cfc5afd1ebbb04a3b7d7351116e Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 9 May 2014 07:14:42 -0400 +Subject: [PATCH 14/24] goaalarm: small memory leak fix + +I was looking through a valgrind log and noticed this: + +==30104== +==30104== 40 bytes in 1 blocks are possibly lost in loss record 1,472 of +2,959 +==30104== at 0x4C2845D: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) +==30104== by 0xB6BD996: standard_malloc (gmem.c:85) +==30104== by 0xB6BDAAC: g_malloc (gmem.c:159) +==30104== by 0xB6DA0CE: g_slice_alloc (gslice.c:1003) +==30104== by 0xB71295A: g_rec_mutex_impl_new (gthread-posix.c:271) +==30104== by 0xB712AA8: g_rec_mutex_init (gthread-posix.c:339) +==30104== by 0x41B7C6: goa_alarm_init (goaalarm.c:174) +==30104== by 0xB4332B7: g_type_create_instance (gtype.c:1917) +==30104== by 0xB416A5D: g_object_constructor (gobject.c:1855) +==30104== by 0xB416149: g_object_newv (gobject.c:1719) +==30104== by 0xB4169B2: g_object_new_valist (gobject.c:1836) +==30104== by 0xB415AB8: g_object_new (gobject.c:1551) + +This commit adds the missing g_rec_mutex_clear call. + +https://bugzilla.gnome.org/show_bug.cgi?id=729864 +--- + src/goaidentity/goaalarm.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 5f50243..0b5aad0 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -146,60 +146,62 @@ clear_scheduled_wakeups (GoaAlarm *self) + case GOA_ALARM_TYPE_TIMEOUT: + clear_scheduled_timeout_wakeups (self); + break; + + default: + break; + } + + g_clear_object (&self->priv->cancellable); + + g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); + + g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); + + g_assert (self->priv->timeout.source == NULL); + + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); + } + + static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + clear_scheduled_wakeups (self); + ++ g_rec_mutex_clear (&self->priv->lock); ++ + G_OBJECT_CLASS (goa_alarm_parent_class)->finalize (object); + } + + static void + goa_alarm_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + GDateTime *time; + + switch (property_id) + { + case PROP_TIME: + time = (GDateTime *) g_value_get_boxed (value); + goa_alarm_set_time (self, time, self->priv->cancellable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *param_spec) + { +-- +1.8.3.1 + + +From 8a4ecda67cc848f1a4a1c212061f66850359a23e Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 9 May 2014 07:29:45 -0400 +Subject: [PATCH 15/24] identity: fix another principal leak + +Spotted by valgrind. + +https://bugzilla.gnome.org/show_bug.cgi?id=729865 +--- + src/goaidentity/goakerberosidentity.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index 9308295..f06bf30 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -216,112 +216,114 @@ goa_kerberos_identity_class_init (GoaKerberosIdentityClass *klass) + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + signals[NEEDS_REFRESH] = g_signal_new ("needs-refresh", + G_TYPE_FROM_CLASS (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_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; ++ char *identifier = NULL; + + if (self->priv->credentials_cache == NULL) + return NULL; + + error_code = krb5_cc_get_principal (self->priv->kerberos_context, + self->priv->credentials_cache, + &principal); + + if (error_code != 0) + { + if (error_code == KRB5_CC_END) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_CREDENTIALS_UNAVAILABLE, + error_code, + _ + ("Could not find identity in credential cache: %k")); + } + else + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ENUMERATING_CREDENTIALS, + error_code, + _ + ("Could not find identity in credential cache: %k")); + } + return NULL; + } + + error_code = krb5_unparse_name_flags (self->priv->kerberos_context, + principal, + 0, + &unparsed_name); + + if (error_code != 0) + { + const char *error_message; + + error_message = + krb5_get_error_message (self->priv->kerberos_context, error_code); + goa_debug ("GoaKerberosIdentity: Error parsing principal identity name: %s", + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); +- return NULL; ++ goto out; + } + + identifier = g_strdup (unparsed_name); + krb5_free_unparsed_name (self->priv->kerberos_context, unparsed_name); + ++out: ++ krb5_free_principal (self->priv->kerberos_context, principal); + return identifier; + } + + static void + goa_kerberos_identity_init (GoaKerberosIdentity *self) + { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GOA_TYPE_KERBEROS_IDENTITY, + GoaKerberosIdentityPrivate); + self->priv->expiration_alarm = goa_alarm_new (); + self->priv->expiring_alarm = goa_alarm_new (); + self->priv->renewal_alarm = goa_alarm_new (); + } + + static void + set_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_message; + char *expanded_format; + va_list args; + char **chunks; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + chunks = g_strsplit (format, "%k", -1); +-- +1.8.3.1 + + +From 63bc1591258f7543a393b1476bd067b79ad3134d Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Fri, 8 Nov 2013 18:33:49 +0100 +Subject: [PATCH 16/24] alarm: Do not clear the wrong objects when setting the + time + +When the time is set using goa_alarm_set_time, we cancel the existing +self->priv->cancellable and replace it with a new one. Then we take a +reference to a new context and time. However, since when we cancelled +the old cancellable, we triggered a chain of events which cause +clear_scheduled_wakeups to be invoked from a idle callback. By the time +it gets invoked, self->priv->cancellable, self->priv->context and +self->priv->time point to the new objects that were set up in +goa_alarm_set_time, which we don't want to clear. + +Actually, there is no point in clearing them in +clear_scheduled_wakeups, because goa_alarm_set_time already clears up +the older objects and the only other time we want to clear them is in +dispose. + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 20 ++++++++++++++------ + 1 file changed, 14 insertions(+), 6 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 0b5aad0..fa6b47d 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -124,134 +124,141 @@ clear_scheduled_timer_wakeups (GoaAlarm *self) + g_clear_object (&self->priv->timer.stream); + #endif + } + + static void + clear_scheduled_timeout_wakeups (GoaAlarm *self) + { + g_clear_pointer (&self->priv->timeout.source, (GDestroyNotify) g_source_destroy); + } + + static void + clear_scheduled_wakeups (GoaAlarm *self) + { + g_rec_mutex_lock (&self->priv->lock); + clear_scheduled_immediate_wakeup (self); + + switch (self->priv->type) + { + case GOA_ALARM_TYPE_TIMER: + clear_scheduled_timer_wakeups (self); + break; + + case GOA_ALARM_TYPE_TIMEOUT: + clear_scheduled_timeout_wakeups (self); + break; + + default: + break; + } + +- g_clear_object (&self->priv->cancellable); +- +- g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); +- + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); + +- g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); +- + g_assert (self->priv->timeout.source == NULL); + + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); + } + + static void ++goa_alarm_dispose (GObject *object) ++{ ++ GoaAlarm *self = GOA_ALARM (object); ++ ++ g_clear_object (&self->priv->cancellable); ++ g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); ++ g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); ++ ++ G_OBJECT_CLASS (goa_alarm_parent_class)->dispose (object); ++} ++ ++static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + clear_scheduled_wakeups (self); + + g_rec_mutex_clear (&self->priv->lock); + + G_OBJECT_CLASS (goa_alarm_parent_class)->finalize (object); + } + + static void + goa_alarm_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + GDateTime *time; + + switch (property_id) + { + case PROP_TIME: + time = (GDateTime *) g_value_get_boxed (value); + goa_alarm_set_time (self, time, self->priv->cancellable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + + switch (property_id) + { + case PROP_TIME: + g_value_set_boxed (value, self->priv->time); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_class_init (GoaAlarmClass *klass) + { + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + ++ object_class->dispose = goa_alarm_dispose; + object_class->finalize = goa_alarm_finalize; + object_class->get_property = goa_alarm_get_property; + object_class->set_property = goa_alarm_set_property; + + g_type_class_add_private (klass, sizeof (GoaAlarmPrivate)); + + signals[FIRED] = g_signal_new ("fired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[REARMED] = g_signal_new ("rearmed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, + PROP_TIME, + g_param_spec_boxed ("time", + _("Time"), + _("Time to fire"), + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + } + + static void + goa_alarm_init (GoaAlarm *self) + { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GOA_TYPE_ALARM, GoaAlarmPrivate); + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; +@@ -612,55 +619,56 @@ goa_alarm_set_time (GoaAlarm *self, GDateTime *time, GCancellable *cancellable) + return; + + g_rec_mutex_lock (&self->priv->lock); + if (self->priv->cancellable != NULL && self->priv->cancellable != cancellable) + g_cancellable_cancel (self->priv->cancellable); + + if (cancellable != NULL) + g_object_ref (cancellable); + + if (self->priv->cancelled_id != 0) + g_cancellable_disconnect (self->priv->cancellable, self->priv->cancelled_id); + + g_clear_object (&self->priv->cancellable); + + if (cancellable != NULL) + self->priv->cancellable = cancellable; + else + self->priv->cancellable = g_cancellable_new (); + + self->priv->cancelled_id = g_cancellable_connect (self->priv->cancellable, + G_CALLBACK (on_cancelled), + self, NULL); + + g_date_time_ref (time); + + if (self->priv->time != NULL) + g_date_time_unref (self->priv->time); + + self->priv->time = time; + ++ g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + self->priv->context = g_main_context_ref (g_main_context_default ()); + + schedule_wakeups (self); + + /* Wake up right away, in case it's already expired leaving the gate */ + schedule_immediate_wakeup (self); + g_rec_mutex_unlock (&self->priv->lock); + g_object_notify (G_OBJECT (self), "time"); + } + + GDateTime * + goa_alarm_get_time (GoaAlarm *self) + { + return self->priv->time; + } + + GoaAlarm * + goa_alarm_new (void) + { + GoaAlarm *self; + + self = GOA_ALARM (g_object_new (GOA_TYPE_ALARM, NULL)); + + return GOA_ALARM (self); + } +-- +1.8.3.1 + + +From 0cf1b2a66d564cf234e401ea86bb3ca4615ff4d8 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Fri, 8 Nov 2013 18:36:46 +0100 +Subject: [PATCH 17/24] alarm: The global default main context is always the + same + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index fa6b47d..18ca90c 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -619,56 +619,56 @@ goa_alarm_set_time (GoaAlarm *self, GDateTime *time, GCancellable *cancellable) + return; + + g_rec_mutex_lock (&self->priv->lock); + if (self->priv->cancellable != NULL && self->priv->cancellable != cancellable) + g_cancellable_cancel (self->priv->cancellable); + + if (cancellable != NULL) + g_object_ref (cancellable); + + if (self->priv->cancelled_id != 0) + g_cancellable_disconnect (self->priv->cancellable, self->priv->cancelled_id); + + g_clear_object (&self->priv->cancellable); + + if (cancellable != NULL) + self->priv->cancellable = cancellable; + else + self->priv->cancellable = g_cancellable_new (); + + self->priv->cancelled_id = g_cancellable_connect (self->priv->cancellable, + G_CALLBACK (on_cancelled), + self, NULL); + + g_date_time_ref (time); + + if (self->priv->time != NULL) + g_date_time_unref (self->priv->time); + + self->priv->time = time; + +- g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); +- self->priv->context = g_main_context_ref (g_main_context_default ()); ++ if (self->priv->context == NULL) ++ self->priv->context = g_main_context_ref (g_main_context_default ()); + + schedule_wakeups (self); + + /* Wake up right away, in case it's already expired leaving the gate */ + schedule_immediate_wakeup (self); + g_rec_mutex_unlock (&self->priv->lock); + g_object_notify (G_OBJECT (self), "time"); + } + + GDateTime * + goa_alarm_get_time (GoaAlarm *self) + { + return self->priv->time; + } + + GoaAlarm * + goa_alarm_new (void) + { + GoaAlarm *self; + + self = GOA_ALARM (g_object_new (GOA_TYPE_ALARM, NULL)); + + return GOA_ALARM (self); + } +-- +1.8.3.1 + + +From ecd08c1b24aa7160f1ab208ea454be5d3b275bab Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 11 Nov 2013 11:51:42 +0100 +Subject: [PATCH 18/24] alarm: Use the same GSource pointer for TIMER and + TIMEOUT alarms + +Simplifies the code so that it is easier to tag the "cancelled" +handler with the correct GSource and GInputStream (if any) that are to +be cleaned. + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 50 ++++++++++++++++------------------------------ + 1 file changed, 17 insertions(+), 33 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 18ca90c..f5a1dfb 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -1,188 +1,172 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* +- * Copyright (C) 2012 Red Hat, Inc. ++ * Copyright (C) 2012, 2013 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Author: Ray Strode + * Based on work by Colin Walters + */ + + #include "config.h" + + #include "goaalarm.h" + + #ifdef HAVE_TIMERFD + #include + #endif + + #include + #include + + #include + #include + #include + #include + + #include "goalogging.h" + +-typedef struct +-{ +- GSource *source; +- GInputStream *stream; +-} Timer; +- +-typedef struct +-{ +- GSource *source; +-} Timeout; +- + #define MAX_TIMEOUT_INTERVAL (10 *1000) + + typedef enum + { + GOA_ALARM_TYPE_UNSCHEDULED, + GOA_ALARM_TYPE_TIMER, + GOA_ALARM_TYPE_TIMEOUT, + } GoaAlarmType; + + struct _GoaAlarmPrivate + { + GCancellable *cancellable; + gulong cancelled_id; + GDateTime *time; + GDateTime *previous_wakeup_time; + GMainContext *context; + GSource *immediate_wakeup_source; + GRecMutex lock; + + GoaAlarmType type; +- union +- { +- Timer timer; +- Timeout timeout; +- }; ++ GSource *scheduled_wakeup_source; ++ GInputStream *stream; /* NULL, unless using timerfd */ + }; + + enum + { + FIRED, + REARMED, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_TIME + }; + + static void schedule_wakeups (GoaAlarm *self); + static void schedule_wakeups_with_timeout_source (GoaAlarm *self); + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + + G_DEFINE_TYPE (GoaAlarm, goa_alarm, G_TYPE_OBJECT); + + static void + clear_scheduled_immediate_wakeup (GoaAlarm *self) + { + g_clear_pointer (&self->priv->immediate_wakeup_source, + (GDestroyNotify) g_source_destroy); + } + + static void + clear_scheduled_timer_wakeups (GoaAlarm *self) + { + #ifdef HAVE_TIMERFD + GError *error; + gboolean is_closed; + +- g_clear_pointer (&self->priv->timer.source, (GDestroyNotify) g_source_destroy); ++ g_clear_pointer (&self->priv->scheduled_wakeup_source, (GDestroyNotify) g_source_destroy); + + error = NULL; +- is_closed = g_input_stream_close (self->priv->timer.stream, NULL, &error); ++ is_closed = g_input_stream_close (self->priv->stream, NULL, &error); + + if (!is_closed) + { + goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); + g_error_free (error); + } + +- g_clear_object (&self->priv->timer.stream); ++ g_clear_object (&self->priv->stream); + #endif + } + + static void + clear_scheduled_timeout_wakeups (GoaAlarm *self) + { +- g_clear_pointer (&self->priv->timeout.source, (GDestroyNotify) g_source_destroy); ++ g_clear_pointer (&self->priv->scheduled_wakeup_source, (GDestroyNotify) g_source_destroy); + } + + static void + clear_scheduled_wakeups (GoaAlarm *self) + { + g_rec_mutex_lock (&self->priv->lock); + clear_scheduled_immediate_wakeup (self); + + switch (self->priv->type) + { + case GOA_ALARM_TYPE_TIMER: + clear_scheduled_timer_wakeups (self); + break; + + case GOA_ALARM_TYPE_TIMEOUT: + clear_scheduled_timeout_wakeups (self); + break; + + default: + break; + } + + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); + +- g_assert (self->priv->timeout.source == NULL); +- + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); + } + + static void + goa_alarm_dispose (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + g_clear_object (&self->priv->cancellable); + g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); + + G_OBJECT_CLASS (goa_alarm_parent_class)->dispose (object); + } + + static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + clear_scheduled_wakeups (self); + + g_rec_mutex_clear (&self->priv->lock); + + G_OBJECT_CLASS (goa_alarm_parent_class)->finalize (object); + } + + static void + goa_alarm_set_property (GObject *object, +@@ -410,188 +394,188 @@ on_timer_source_ready (GObject *stream, GTask *task) + if (bytes_read < 0) + { + goa_warning ("GoaAlarm: failed to read from timer fd: %s\n", + error->message); + g_error_free (error); + goto out; + } + + if (bytes_read == sizeof (gint64)) + { + if (number_of_fires < 0 || number_of_fires > 1) + { + goa_warning ("GoaAlarm: expected timerfd to report firing once," + "but it reported firing %ld times\n", (long) number_of_fires); + } + } + + fire_or_rearm_alarm (self); + run_again = TRUE; + out: + g_rec_mutex_unlock (&self->priv->lock); + return run_again; + } + + static void + clear_timer_source (GTask *task) + { + GoaAlarm *self; + + self = g_task_get_source_object (task); +- self->priv->timer.source = NULL; ++ self->priv->scheduled_wakeup_source = NULL; + + g_object_unref (task); + } + #endif + + static gboolean + schedule_wakeups_with_timerfd (GoaAlarm *self) + { + #ifdef HAVE_TIMERFD + struct itimerspec timer_spec; + int fd; + int result; + GSource *source; + GTask *task; + static gboolean seen_before = FALSE; + + if (!seen_before) + { + goa_debug ("GoaAlarm: trying to use kernel timer"); + seen_before = TRUE; + } + + fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK); + + if (fd < 0) + { + goa_debug ("GoaAlarm: could not create timer fd: %m"); + return FALSE; + } + + memset (&timer_spec, 0, sizeof (timer_spec)); + timer_spec.it_value.tv_sec = g_date_time_to_unix (self->priv->time) + 1; + + result = timerfd_settime (fd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &timer_spec, NULL); + + if (result < 0) + { + goa_debug ("GoaAlarm: could not set timer: %m"); + return FALSE; + } + + self->priv->type = GOA_ALARM_TYPE_TIMER; +- self->priv->timer.stream = g_unix_input_stream_new (fd, TRUE); ++ self->priv->stream = g_unix_input_stream_new (fd, TRUE); + + task = g_task_new (self, self->priv->cancellable, NULL, NULL); + + source = + g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM +- (self->priv->timer.stream), ++ (self->priv->stream), + self->priv->cancellable); +- self->priv->timer.source = source; +- g_source_set_callback (self->priv->timer.source, ++ self->priv->scheduled_wakeup_source = source; ++ g_source_set_callback (self->priv->scheduled_wakeup_source, + (GSourceFunc) on_timer_source_ready, task, + (GDestroyNotify) clear_timer_source); +- g_source_attach (self->priv->timer.source, self->priv->context); ++ g_source_attach (self->priv->scheduled_wakeup_source, self->priv->context); + g_source_unref (source); + + return TRUE; + + #endif /*HAVE_TIMERFD */ + + return FALSE; + } + + static gboolean + on_timeout_source_ready (GoaAlarm *self) + { + g_return_val_if_fail (GOA_IS_ALARM (self), FALSE); + + g_rec_mutex_lock (&self->priv->lock); + + if (g_cancellable_is_cancelled (self->priv->cancellable) || + self->priv->type == GOA_ALARM_TYPE_UNSCHEDULED) + goto out; + + fire_or_rearm_alarm (self); + + if (g_cancellable_is_cancelled (self->priv->cancellable)) + goto out; + + schedule_wakeups_with_timeout_source (self); + + out: + g_rec_mutex_unlock (&self->priv->lock); + return FALSE; + } + + static void + clear_timeout_source_pointer (GoaAlarm *self) + { +- self->priv->timeout.source = NULL; ++ self->priv->scheduled_wakeup_source = NULL; + } + + static void + schedule_wakeups_with_timeout_source (GoaAlarm *self) + { + GDateTime *now; + GSource *source; + GTimeSpan time_span; + guint interval; + + self->priv->type = GOA_ALARM_TYPE_TIMEOUT; + + now = g_date_time_new_now_local (); + time_span = g_date_time_difference (self->priv->time, now); + g_date_time_unref (now); + + time_span = + CLAMP (time_span, 1000 *G_TIME_SPAN_MILLISECOND, + G_MAXUINT *G_TIME_SPAN_MILLISECOND); + interval = (guint) time_span / G_TIME_SPAN_MILLISECOND; + + /* We poll every 10 seconds or so because we want to catch time skew + */ + interval = MIN (interval, MAX_TIMEOUT_INTERVAL); + + source = g_timeout_source_new (interval); + +- self->priv->timeout.source = source; +- g_source_set_callback (self->priv->timeout.source, ++ self->priv->scheduled_wakeup_source = source; ++ g_source_set_callback (self->priv->scheduled_wakeup_source, + (GSourceFunc) + on_timeout_source_ready, + self, (GDestroyNotify) clear_timeout_source_pointer); + +- g_source_attach (self->priv->timeout.source, self->priv->context); ++ g_source_attach (self->priv->scheduled_wakeup_source, self->priv->context); + g_source_unref (source); + } + + static void + schedule_wakeups (GoaAlarm *self) + { + gboolean wakeup_scheduled; + + wakeup_scheduled = schedule_wakeups_with_timerfd (self); + + if (!wakeup_scheduled) + { + static gboolean seen_before = FALSE; + + if (!seen_before) + { + goa_debug ("GoaAlarm: falling back to polling timeout"); + seen_before = TRUE; + } + schedule_wakeups_with_timeout_source (self); + } + } + + static void + clear_immediate_wakeup_source_pointer (GoaAlarm *self) + { + self->priv->immediate_wakeup_source = NULL; + } + + static void +-- +1.8.3.1 + + +From 553fea89083bbaa32a96cee23eede4317ecc817d Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 11 Nov 2013 13:11:28 +0100 +Subject: [PATCH 19/24] alarm: Tag the "cancelled" handler with the correct + source and stream + +When a time is set using goa_alarm_set_time, we try to clear the older +source and stream by cancelling self->priv->cancellable. However, +before we have had a chance to actually clear them in an idle callback, +a new source and stream corresponding to the new time is set. This +causes the older source and stream to be leaked, and instead the newly +created objects are cleared. + +This is wrong. To set things right, we tag the cancelled handler with +the older source and stream before they are overwritten. + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 60 +++++++++++++++++++++++++++------------------- + 1 file changed, 35 insertions(+), 25 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index f5a1dfb..68a6c46 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -63,132 +63,132 @@ struct _GoaAlarmPrivate + GInputStream *stream; /* NULL, unless using timerfd */ + }; + + enum + { + FIRED, + REARMED, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_TIME + }; + + static void schedule_wakeups (GoaAlarm *self); + static void schedule_wakeups_with_timeout_source (GoaAlarm *self); + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + + G_DEFINE_TYPE (GoaAlarm, goa_alarm, G_TYPE_OBJECT); + + static void + clear_scheduled_immediate_wakeup (GoaAlarm *self) + { + g_clear_pointer (&self->priv->immediate_wakeup_source, + (GDestroyNotify) g_source_destroy); + } + + static void +-clear_scheduled_timer_wakeups (GoaAlarm *self) ++clear_scheduled_timer_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) + { + #ifdef HAVE_TIMERFD + GError *error; + gboolean is_closed; + +- g_clear_pointer (&self->priv->scheduled_wakeup_source, (GDestroyNotify) g_source_destroy); ++ g_source_destroy (source); + + error = NULL; +- is_closed = g_input_stream_close (self->priv->stream, NULL, &error); ++ is_closed = g_input_stream_close (stream, NULL, &error); + + if (!is_closed) + { + goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); + g_error_free (error); + } + +- g_clear_object (&self->priv->stream); ++ g_object_unref (stream); + #endif + } + + static void +-clear_scheduled_timeout_wakeups (GoaAlarm *self) ++clear_scheduled_timeout_wakeups (GoaAlarm *self, GSource *source) + { +- g_clear_pointer (&self->priv->scheduled_wakeup_source, (GDestroyNotify) g_source_destroy); ++ g_source_destroy (source); + } + + static void +-clear_scheduled_wakeups (GoaAlarm *self) ++clear_scheduled_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) + { + g_rec_mutex_lock (&self->priv->lock); + clear_scheduled_immediate_wakeup (self); + + switch (self->priv->type) + { + case GOA_ALARM_TYPE_TIMER: +- clear_scheduled_timer_wakeups (self); ++ clear_scheduled_timer_wakeups (self, source, stream); + break; + + case GOA_ALARM_TYPE_TIMEOUT: +- clear_scheduled_timeout_wakeups (self); ++ clear_scheduled_timeout_wakeups (self, source); + break; + + default: + break; + } + + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); + + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); + } + + static void + goa_alarm_dispose (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + g_clear_object (&self->priv->cancellable); + g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); + + G_OBJECT_CLASS (goa_alarm_parent_class)->dispose (object); + } + + static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + +- clear_scheduled_wakeups (self); ++ clear_scheduled_wakeups (self, self->priv->scheduled_wakeup_source, self->priv->stream); + + g_rec_mutex_clear (&self->priv->lock); + + G_OBJECT_CLASS (goa_alarm_parent_class)->finalize (object); + } + + static void + goa_alarm_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + GDateTime *time; + + switch (property_id) + { + case PROP_TIME: + time = (GDateTime *) g_value_get_boxed (value); + goa_alarm_set_time (self, time, self->priv->cancellable); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_get_property (GObject *object, + guint property_id, +@@ -225,78 +225,99 @@ goa_alarm_class_init (GoaAlarmClass *klass) + signals[FIRED] = g_signal_new ("fired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[REARMED] = g_signal_new ("rearmed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, + PROP_TIME, + g_param_spec_boxed ("time", + _("Time"), + _("Time to fire"), + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + } + + static void + goa_alarm_init (GoaAlarm *self) + { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GOA_TYPE_ALARM, GoaAlarmPrivate); + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_init (&self->priv->lock); + } + + static gboolean + async_alarm_cancel_idle_cb (gpointer user_data) + { +- GoaAlarm *self = user_data; ++ GoaAlarm *self; ++ GInputStream *stream; ++ GSource *source; ++ GTask *task = G_TASK (user_data); ++ gpointer task_data; + +- clear_scheduled_wakeups (self); ++ self = g_task_get_source_object (task); ++ source = (GSource *) g_object_get_data (G_OBJECT (task), "alarm-scheduled-wakeup-source"); ++ task_data = g_object_get_data (G_OBJECT (task), "alarm-stream"); ++ stream = (task_data == NULL) ? NULL : G_INPUT_STREAM (task_data); ++ ++ clear_scheduled_wakeups (self, source, stream); + return G_SOURCE_REMOVE; + } + + static void + on_cancelled (GCancellable *cancellable, gpointer user_data) + { + GoaAlarm *self = GOA_ALARM (user_data); + GSource *idle_source; ++ GTask *task; + ++ task = g_task_new (self, NULL, NULL, NULL); ++ ++ g_object_set_data_full (G_OBJECT (task), ++ "alarm-scheduled-wakeup-source", ++ g_source_ref (self->priv->scheduled_wakeup_source), ++ (GDestroyNotify) g_source_unref); ++ ++ if (self->priv->stream != NULL) ++ g_object_set_data_full (G_OBJECT (task), "alarm-stream", g_object_ref (self->priv->stream), g_object_unref); + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE); +- g_source_set_callback (idle_source, async_alarm_cancel_idle_cb, g_object_ref (self), g_object_unref); ++ g_source_set_callback (idle_source, async_alarm_cancel_idle_cb, g_object_ref (task), g_object_unref); + g_source_attach (idle_source, self->priv->context); + g_source_unref (idle_source); ++ ++ g_object_unref (task); + } + + static void + fire_alarm (GoaAlarm *self) + { + g_signal_emit (G_OBJECT (self), signals[FIRED], 0); + } + + static void + rearm_alarm (GoaAlarm *self) + { + g_signal_emit (G_OBJECT (self), signals[REARMED], 0); + } + + static void + fire_or_rearm_alarm (GoaAlarm *self) + { + GTimeSpan time_until_fire; + GTimeSpan previous_time_until_fire; + GDateTime *now; + + now = g_date_time_new_now_local (); + time_until_fire = g_date_time_difference (self->priv->time, now); + + if (self->priv->previous_wakeup_time == NULL) + { + self->priv->previous_wakeup_time = now; + + /* If, according to the time, we're past when we should have fired, + * then fire the alarm. +@@ -387,124 +408,113 @@ on_timer_source_ready (GObject *stream, GTask *task) + goto out; + + bytes_read = + g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream), + &number_of_fires, sizeof (gint64), + NULL, &error); + + if (bytes_read < 0) + { + goa_warning ("GoaAlarm: failed to read from timer fd: %s\n", + error->message); + g_error_free (error); + goto out; + } + + if (bytes_read == sizeof (gint64)) + { + if (number_of_fires < 0 || number_of_fires > 1) + { + goa_warning ("GoaAlarm: expected timerfd to report firing once," + "but it reported firing %ld times\n", (long) number_of_fires); + } + } + + fire_or_rearm_alarm (self); + run_again = TRUE; + out: + g_rec_mutex_unlock (&self->priv->lock); + return run_again; + } +- +-static void +-clear_timer_source (GTask *task) +-{ +- GoaAlarm *self; +- +- self = g_task_get_source_object (task); +- self->priv->scheduled_wakeup_source = NULL; +- +- g_object_unref (task); +-} + #endif + + static gboolean + schedule_wakeups_with_timerfd (GoaAlarm *self) + { + #ifdef HAVE_TIMERFD + struct itimerspec timer_spec; + int fd; + int result; + GSource *source; + GTask *task; + static gboolean seen_before = FALSE; + + if (!seen_before) + { + goa_debug ("GoaAlarm: trying to use kernel timer"); + seen_before = TRUE; + } + + fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK); + + if (fd < 0) + { + goa_debug ("GoaAlarm: could not create timer fd: %m"); + return FALSE; + } + + memset (&timer_spec, 0, sizeof (timer_spec)); + timer_spec.it_value.tv_sec = g_date_time_to_unix (self->priv->time) + 1; + + result = timerfd_settime (fd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &timer_spec, NULL); + + if (result < 0) + { + goa_debug ("GoaAlarm: could not set timer: %m"); + return FALSE; + } + + self->priv->type = GOA_ALARM_TYPE_TIMER; + self->priv->stream = g_unix_input_stream_new (fd, TRUE); + + task = g_task_new (self, self->priv->cancellable, NULL, NULL); + + source = + g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM + (self->priv->stream), + self->priv->cancellable); + self->priv->scheduled_wakeup_source = source; + g_source_set_callback (self->priv->scheduled_wakeup_source, + (GSourceFunc) on_timer_source_ready, task, +- (GDestroyNotify) clear_timer_source); ++ (GDestroyNotify) g_object_unref); + g_source_attach (self->priv->scheduled_wakeup_source, self->priv->context); + g_source_unref (source); + + return TRUE; + + #endif /*HAVE_TIMERFD */ + + return FALSE; + } + + static gboolean + on_timeout_source_ready (GoaAlarm *self) + { + g_return_val_if_fail (GOA_IS_ALARM (self), FALSE); + + g_rec_mutex_lock (&self->priv->lock); + + if (g_cancellable_is_cancelled (self->priv->cancellable) || + self->priv->type == GOA_ALARM_TYPE_UNSCHEDULED) + goto out; + + fire_or_rearm_alarm (self); + + if (g_cancellable_is_cancelled (self->priv->cancellable)) + goto out; + + schedule_wakeups_with_timeout_source (self); + + out: + g_rec_mutex_unlock (&self->priv->lock); +-- +1.8.3.1 + + +From 18e305912fbac9c56c21d0c5633a095ac4fe4176 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 11 Nov 2013 14:49:06 +0100 +Subject: [PATCH 20/24] alarm: Remove redundant preprocessor conditional + +Reading the rest of the code, it appears to me that the preprocessor +conditionals were meant to surpress compiler warnings and errors +caused by the lack of timerfd support. For the rest of the logic, +GoaAlarmType is used to separate the different kinds of timers. + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 68a6c46..4381f0b 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -65,77 +65,75 @@ struct _GoaAlarmPrivate + + enum + { + FIRED, + REARMED, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_TIME + }; + + static void schedule_wakeups (GoaAlarm *self); + static void schedule_wakeups_with_timeout_source (GoaAlarm *self); + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + + G_DEFINE_TYPE (GoaAlarm, goa_alarm, G_TYPE_OBJECT); + + static void + clear_scheduled_immediate_wakeup (GoaAlarm *self) + { + g_clear_pointer (&self->priv->immediate_wakeup_source, + (GDestroyNotify) g_source_destroy); + } + + static void + clear_scheduled_timer_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) + { +-#ifdef HAVE_TIMERFD + GError *error; + gboolean is_closed; + + g_source_destroy (source); + + error = NULL; + is_closed = g_input_stream_close (stream, NULL, &error); + + if (!is_closed) + { + goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); + g_error_free (error); + } + + g_object_unref (stream); +-#endif + } + + static void + clear_scheduled_timeout_wakeups (GoaAlarm *self, GSource *source) + { + g_source_destroy (source); + } + + static void + clear_scheduled_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) + { + g_rec_mutex_lock (&self->priv->lock); + clear_scheduled_immediate_wakeup (self); + + switch (self->priv->type) + { + case GOA_ALARM_TYPE_TIMER: + clear_scheduled_timer_wakeups (self, source, stream); + break; + + case GOA_ALARM_TYPE_TIMEOUT: + clear_scheduled_timeout_wakeups (self, source); + break; + + default: + break; + } + + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); +-- +1.8.3.1 + + +From 108f318ecadad481dfd38b619d56c575484745bf Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Tue, 12 Nov 2013 11:00:05 +0100 +Subject: [PATCH 21/24] alarm: Consolidate clear_scheduled_time*_wakeups into + one function + +Since we are using the same GSource pointer for TIMER and TIMEOUT +alarms, there is no reason to continue having separate functions. + +Fixes: https://bugzilla.gnome.org/711696 +--- + src/goaidentity/goaalarm.c | 52 +++++++++++++++------------------------------- + 1 file changed, 17 insertions(+), 35 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 4381f0b..34d99f3 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -63,103 +63,85 @@ struct _GoaAlarmPrivate + GInputStream *stream; /* NULL, unless using timerfd */ + }; + + enum + { + FIRED, + REARMED, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_TIME + }; + + static void schedule_wakeups (GoaAlarm *self); + static void schedule_wakeups_with_timeout_source (GoaAlarm *self); + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + + G_DEFINE_TYPE (GoaAlarm, goa_alarm, G_TYPE_OBJECT); + + static void + clear_scheduled_immediate_wakeup (GoaAlarm *self) + { + g_clear_pointer (&self->priv->immediate_wakeup_source, + (GDestroyNotify) g_source_destroy); + } + + static void +-clear_scheduled_timer_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) +-{ +- GError *error; +- gboolean is_closed; +- +- g_source_destroy (source); +- +- error = NULL; +- is_closed = g_input_stream_close (stream, NULL, &error); +- +- if (!is_closed) +- { +- goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); +- g_error_free (error); +- } +- +- g_object_unref (stream); +-} +- +-static void +-clear_scheduled_timeout_wakeups (GoaAlarm *self, GSource *source) +-{ +- g_source_destroy (source); +-} +- +-static void + clear_scheduled_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) + { + g_rec_mutex_lock (&self->priv->lock); + clear_scheduled_immediate_wakeup (self); + +- switch (self->priv->type) ++ if (self->priv->type != GOA_ALARM_TYPE_UNSCHEDULED) + { +- case GOA_ALARM_TYPE_TIMER: +- clear_scheduled_timer_wakeups (self, source, stream); +- break; ++ g_source_destroy (source); + +- case GOA_ALARM_TYPE_TIMEOUT: +- clear_scheduled_timeout_wakeups (self, source); +- break; ++ if (stream != NULL) ++ { ++ GError *error; ++ gboolean is_closed; + +- default: +- break; ++ error = NULL; ++ is_closed = g_input_stream_close (stream, NULL, &error); ++ ++ if (!is_closed) ++ { ++ goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); ++ g_error_free (error); ++ } ++ ++ g_object_unref (stream); ++ } + } + + g_clear_pointer (&self->priv->previous_wakeup_time, + (GDestroyNotify) g_date_time_unref); + + self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_unlock (&self->priv->lock); + } + + static void + goa_alarm_dispose (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + g_clear_object (&self->priv->cancellable); + g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); + + G_OBJECT_CLASS (goa_alarm_parent_class)->dispose (object); + } + + static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + + clear_scheduled_wakeups (self, self->priv->scheduled_wakeup_source, self->priv->stream); + + g_rec_mutex_clear (&self->priv->lock); + +-- +1.8.3.1 + + +From 34fcfc59b52f18fac0ab8d94808aed55e10d5173 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Thu, 8 May 2014 13:31:16 -0400 +Subject: [PATCH 22/24] identity: dramatically simplify alarm logic + +The GoaAlarm code is unwieldy. Primarily this is because +it supports resetting the alarm after creation, but can't +clean up old state right away due to a bug in GLib +(bug 705395). All this complexitly is leading to bugs, and +caos. + +This commit introduces some zen to the situation by making +GoaAlarm immutable and much simpler. Now to reset an alarm, +the identity code just instantiates a new one and destroys the +old one. + +https://bugzilla.gnome.org/show_bug.cgi?id=729718 +--- + src/goaidentity/goaalarm.c | 177 ++++------------------------------ + src/goaidentity/goaalarm.h | 5 +- + src/goaidentity/goakerberosidentity.c | 131 ++++++++++++------------- + 3 files changed, 82 insertions(+), 231 deletions(-) + +diff --git a/src/goaidentity/goaalarm.c b/src/goaidentity/goaalarm.c +index 34d99f3..c86f665 100644 +--- a/src/goaidentity/goaalarm.c ++++ b/src/goaidentity/goaalarm.c +@@ -23,172 +23,131 @@ + + #include "config.h" + + #include "goaalarm.h" + + #ifdef HAVE_TIMERFD + #include + #endif + + #include + #include + + #include + #include + #include + #include + + #include "goalogging.h" + + #define MAX_TIMEOUT_INTERVAL (10 *1000) + + typedef enum + { + GOA_ALARM_TYPE_UNSCHEDULED, + GOA_ALARM_TYPE_TIMER, + GOA_ALARM_TYPE_TIMEOUT, + } GoaAlarmType; + + struct _GoaAlarmPrivate + { +- GCancellable *cancellable; +- gulong cancelled_id; + GDateTime *time; + GDateTime *previous_wakeup_time; + GMainContext *context; + GSource *immediate_wakeup_source; + GRecMutex lock; + + GoaAlarmType type; + GSource *scheduled_wakeup_source; + GInputStream *stream; /* NULL, unless using timerfd */ + }; + + enum + { + FIRED, + REARMED, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_TIME + }; + + static void schedule_wakeups (GoaAlarm *self); + static void schedule_wakeups_with_timeout_source (GoaAlarm *self); ++static void goa_alarm_set_time (GoaAlarm *self, GDateTime *time); ++static void clear_wakeup_source_pointer (GoaAlarm *self); + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; + + G_DEFINE_TYPE (GoaAlarm, goa_alarm, G_TYPE_OBJECT); + + static void +-clear_scheduled_immediate_wakeup (GoaAlarm *self) +-{ +- g_clear_pointer (&self->priv->immediate_wakeup_source, +- (GDestroyNotify) g_source_destroy); +-} +- +-static void +-clear_scheduled_wakeups (GoaAlarm *self, GSource *source, GInputStream *stream) +-{ +- g_rec_mutex_lock (&self->priv->lock); +- clear_scheduled_immediate_wakeup (self); +- +- if (self->priv->type != GOA_ALARM_TYPE_UNSCHEDULED) +- { +- g_source_destroy (source); +- +- if (stream != NULL) +- { +- GError *error; +- gboolean is_closed; +- +- error = NULL; +- is_closed = g_input_stream_close (stream, NULL, &error); +- +- if (!is_closed) +- { +- goa_warning ("GoaAlarm: could not close timer stream: %s", error->message); +- g_error_free (error); +- } +- +- g_object_unref (stream); +- } +- } +- +- g_clear_pointer (&self->priv->previous_wakeup_time, +- (GDestroyNotify) g_date_time_unref); +- +- self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; +- g_rec_mutex_unlock (&self->priv->lock); +-} +- +-static void + goa_alarm_dispose (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + +- g_clear_object (&self->priv->cancellable); ++ g_clear_object (&self->priv->stream); ++ g_clear_pointer (&self->priv->immediate_wakeup_source, (GDestroyNotify) g_source_destroy); ++ g_clear_pointer (&self->priv->scheduled_wakeup_source, (GDestroyNotify) g_source_destroy); + g_clear_pointer (&self->priv->context, (GDestroyNotify) g_main_context_unref); + g_clear_pointer (&self->priv->time, (GDestroyNotify) g_date_time_unref); ++ g_clear_pointer (&self->priv->previous_wakeup_time, (GDestroyNotify) g_date_time_unref); + + G_OBJECT_CLASS (goa_alarm_parent_class)->dispose (object); + } + + static void + goa_alarm_finalize (GObject *object) + { + GoaAlarm *self = GOA_ALARM (object); + +- clear_scheduled_wakeups (self, self->priv->scheduled_wakeup_source, self->priv->stream); +- + g_rec_mutex_clear (&self->priv->lock); + + G_OBJECT_CLASS (goa_alarm_parent_class)->finalize (object); + } + + static void + goa_alarm_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + GDateTime *time; + + switch (property_id) + { + case PROP_TIME: + time = (GDateTime *) g_value_get_boxed (value); +- goa_alarm_set_time (self, time, self->priv->cancellable); ++ goa_alarm_set_time (self, time); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *param_spec) + { + GoaAlarm *self = GOA_ALARM (object); + + switch (property_id) + { + case PROP_TIME: + g_value_set_boxed (value, self->priv->time); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); + break; + } + } + + static void + goa_alarm_class_init (GoaAlarmClass *klass) + { + GObjectClass *object_class; +@@ -198,108 +157,63 @@ goa_alarm_class_init (GoaAlarmClass *klass) + object_class->dispose = goa_alarm_dispose; + object_class->finalize = goa_alarm_finalize; + object_class->get_property = goa_alarm_get_property; + object_class->set_property = goa_alarm_set_property; + + g_type_class_add_private (klass, sizeof (GoaAlarmPrivate)); + + signals[FIRED] = g_signal_new ("fired", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[REARMED] = g_signal_new ("rearmed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + g_object_class_install_property (object_class, + PROP_TIME, + g_param_spec_boxed ("time", + _("Time"), + _("Time to fire"), + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + } + + static void + goa_alarm_init (GoaAlarm *self) + { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GOA_TYPE_ALARM, GoaAlarmPrivate); +- self->priv->type = GOA_ALARM_TYPE_UNSCHEDULED; + g_rec_mutex_init (&self->priv->lock); + } + +-static gboolean +-async_alarm_cancel_idle_cb (gpointer user_data) +-{ +- GoaAlarm *self; +- GInputStream *stream; +- GSource *source; +- GTask *task = G_TASK (user_data); +- gpointer task_data; +- +- self = g_task_get_source_object (task); +- source = (GSource *) g_object_get_data (G_OBJECT (task), "alarm-scheduled-wakeup-source"); +- task_data = g_object_get_data (G_OBJECT (task), "alarm-stream"); +- stream = (task_data == NULL) ? NULL : G_INPUT_STREAM (task_data); +- +- clear_scheduled_wakeups (self, source, stream); +- return G_SOURCE_REMOVE; +-} +- +-static void +-on_cancelled (GCancellable *cancellable, gpointer user_data) +-{ +- GoaAlarm *self = GOA_ALARM (user_data); +- GSource *idle_source; +- GTask *task; +- +- task = g_task_new (self, NULL, NULL, NULL); +- +- g_object_set_data_full (G_OBJECT (task), +- "alarm-scheduled-wakeup-source", +- g_source_ref (self->priv->scheduled_wakeup_source), +- (GDestroyNotify) g_source_unref); +- +- if (self->priv->stream != NULL) +- g_object_set_data_full (G_OBJECT (task), "alarm-stream", g_object_ref (self->priv->stream), g_object_unref); +- +- idle_source = g_idle_source_new (); +- g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE); +- g_source_set_callback (idle_source, async_alarm_cancel_idle_cb, g_object_ref (task), g_object_unref); +- g_source_attach (idle_source, self->priv->context); +- g_source_unref (idle_source); +- +- g_object_unref (task); +-} +- + static void + fire_alarm (GoaAlarm *self) + { + g_signal_emit (G_OBJECT (self), signals[FIRED], 0); + } + + static void + rearm_alarm (GoaAlarm *self) + { + g_signal_emit (G_OBJECT (self), signals[REARMED], 0); + } + + static void + fire_or_rearm_alarm (GoaAlarm *self) + { + GTimeSpan time_until_fire; + GTimeSpan previous_time_until_fire; + GDateTime *now; + + now = g_date_time_new_now_local (); + time_until_fire = g_date_time_difference (self->priv->time, now); + + if (self->priv->previous_wakeup_time == NULL) + { + self->priv->previous_wakeup_time = now; + + /* If, according to the time, we're past when we should have fired, + * then fire the alarm. + */ + if (time_until_fire <= 0) +@@ -314,335 +228,282 @@ fire_or_rearm_alarm (GoaAlarm *self) + g_date_time_unref (self->priv->previous_wakeup_time); + self->priv->previous_wakeup_time = now; + + /* If, according to the time, we're past when we should have fired, + * and this is the first wakeup where that's been true then fire + * the alarm. The first check makes sure we don't fire prematurely, + * and the second check makes sure we don't fire more than once + */ + if (time_until_fire <= 0 && previous_time_until_fire > 0) + { + fire_alarm (self); + + /* If, according to the time, we're before when we should fire, + * and we previously fired the alarm, then we've jumped back in + * time and need to rearm the alarm. + */ + } + else if (time_until_fire > 0 && previous_time_until_fire <= 0) + { + rearm_alarm (self); + } + } + } + + static gboolean + on_immediate_wakeup_source_ready (GoaAlarm *self) + { + g_return_val_if_fail (self->priv->type != GOA_ALARM_TYPE_UNSCHEDULED, FALSE); + + g_rec_mutex_lock (&self->priv->lock); +- if (g_cancellable_is_cancelled (self->priv->cancellable)) +- goto out; +- + fire_or_rearm_alarm (self); +- +-out: + g_rec_mutex_unlock (&self->priv->lock); + return FALSE; + } + + #ifdef HAVE_TIMERFD + static gboolean +-on_timer_source_ready (GObject *stream, GTask *task) ++on_timer_source_ready (GObject *stream, GoaAlarm *self) + { + gint64 number_of_fires; + gssize bytes_read; + gboolean run_again = FALSE; + GError *error = NULL; +- GoaAlarm *self; +- GCancellable *cancellable; +- +- self = g_task_get_source_object (task); +- cancellable = g_task_get_cancellable (task); + + g_return_val_if_fail (GOA_IS_ALARM (self), FALSE); + + g_rec_mutex_lock (&self->priv->lock); + +- if (self->priv->type == GOA_ALARM_TYPE_UNSCHEDULED) +- { +- goa_debug ("GoaAlarm: timer source was unscheduled after " +- "callback was invoked, but before callback got " +- "the lock."); +- goto out; +- } +- else if (self->priv->type != GOA_ALARM_TYPE_TIMER) ++ if (self->priv->type != GOA_ALARM_TYPE_TIMER) + { + goa_warning ("GoaAlarm: timer source ready callback called " + "when timer source isn't supposed to be used. " + "Current timer type is %u", self->priv->type); + goto out; + } + +- if (g_cancellable_is_cancelled (cancellable)) +- goto out; +- + bytes_read = + g_pollable_input_stream_read_nonblocking (G_POLLABLE_INPUT_STREAM (stream), + &number_of_fires, sizeof (gint64), + NULL, &error); + + if (bytes_read < 0) + { + goa_warning ("GoaAlarm: failed to read from timer fd: %s\n", + error->message); + g_error_free (error); + goto out; + } + + if (bytes_read == sizeof (gint64)) + { + if (number_of_fires < 0 || number_of_fires > 1) + { + goa_warning ("GoaAlarm: expected timerfd to report firing once," + "but it reported firing %ld times\n", (long) number_of_fires); + } + } + + fire_or_rearm_alarm (self); + run_again = TRUE; + out: + g_rec_mutex_unlock (&self->priv->lock); + return run_again; + } + #endif + + static gboolean + schedule_wakeups_with_timerfd (GoaAlarm *self) + { + #ifdef HAVE_TIMERFD + struct itimerspec timer_spec; + int fd; + int result; + GSource *source; +- GTask *task; + static gboolean seen_before = FALSE; + + if (!seen_before) + { + goa_debug ("GoaAlarm: trying to use kernel timer"); + seen_before = TRUE; + } + + fd = timerfd_create (CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK); + + if (fd < 0) + { + goa_debug ("GoaAlarm: could not create timer fd: %m"); + return FALSE; + } + + memset (&timer_spec, 0, sizeof (timer_spec)); + timer_spec.it_value.tv_sec = g_date_time_to_unix (self->priv->time) + 1; + + result = timerfd_settime (fd, + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, + &timer_spec, NULL); + + if (result < 0) + { + goa_debug ("GoaAlarm: could not set timer: %m"); + return FALSE; + } + + self->priv->type = GOA_ALARM_TYPE_TIMER; + self->priv->stream = g_unix_input_stream_new (fd, TRUE); + +- task = g_task_new (self, self->priv->cancellable, NULL, NULL); +- + source = + g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM + (self->priv->stream), +- self->priv->cancellable); ++ NULL); + self->priv->scheduled_wakeup_source = source; + g_source_set_callback (self->priv->scheduled_wakeup_source, +- (GSourceFunc) on_timer_source_ready, task, +- (GDestroyNotify) g_object_unref); ++ (GSourceFunc) on_timer_source_ready, self, ++ (GDestroyNotify) clear_wakeup_source_pointer); + g_source_attach (self->priv->scheduled_wakeup_source, self->priv->context); + g_source_unref (source); + + return TRUE; + + #endif /*HAVE_TIMERFD */ + + return FALSE; + } + + static gboolean + on_timeout_source_ready (GoaAlarm *self) + { + g_return_val_if_fail (GOA_IS_ALARM (self), FALSE); + + g_rec_mutex_lock (&self->priv->lock); + +- if (g_cancellable_is_cancelled (self->priv->cancellable) || +- self->priv->type == GOA_ALARM_TYPE_UNSCHEDULED) ++ if (self->priv->type == GOA_ALARM_TYPE_UNSCHEDULED) + goto out; + + fire_or_rearm_alarm (self); + +- if (g_cancellable_is_cancelled (self->priv->cancellable)) +- goto out; +- + schedule_wakeups_with_timeout_source (self); + + out: + g_rec_mutex_unlock (&self->priv->lock); + return FALSE; + } + + static void +-clear_timeout_source_pointer (GoaAlarm *self) ++clear_wakeup_source_pointer (GoaAlarm *self) + { + self->priv->scheduled_wakeup_source = NULL; + } + + static void + schedule_wakeups_with_timeout_source (GoaAlarm *self) + { + GDateTime *now; + GSource *source; + GTimeSpan time_span; + guint interval; + + self->priv->type = GOA_ALARM_TYPE_TIMEOUT; + + now = g_date_time_new_now_local (); + time_span = g_date_time_difference (self->priv->time, now); + g_date_time_unref (now); + + time_span = + CLAMP (time_span, 1000 *G_TIME_SPAN_MILLISECOND, + G_MAXUINT *G_TIME_SPAN_MILLISECOND); + interval = (guint) time_span / G_TIME_SPAN_MILLISECOND; + + /* We poll every 10 seconds or so because we want to catch time skew + */ + interval = MIN (interval, MAX_TIMEOUT_INTERVAL); + + source = g_timeout_source_new (interval); + + self->priv->scheduled_wakeup_source = source; + g_source_set_callback (self->priv->scheduled_wakeup_source, + (GSourceFunc) + on_timeout_source_ready, +- self, (GDestroyNotify) clear_timeout_source_pointer); ++ self, (GDestroyNotify) clear_wakeup_source_pointer); + + g_source_attach (self->priv->scheduled_wakeup_source, self->priv->context); + g_source_unref (source); + } + + static void + schedule_wakeups (GoaAlarm *self) + { + gboolean wakeup_scheduled; + + wakeup_scheduled = schedule_wakeups_with_timerfd (self); + + if (!wakeup_scheduled) + { + static gboolean seen_before = FALSE; + + if (!seen_before) + { + goa_debug ("GoaAlarm: falling back to polling timeout"); + seen_before = TRUE; + } + schedule_wakeups_with_timeout_source (self); + } + } + + static void + clear_immediate_wakeup_source_pointer (GoaAlarm *self) + { + self->priv->immediate_wakeup_source = NULL; + } + + static void + schedule_immediate_wakeup (GoaAlarm *self) + { + GSource *source; + + source = g_idle_source_new (); + + self->priv->immediate_wakeup_source = source; + g_source_set_callback (self->priv->immediate_wakeup_source, + (GSourceFunc) + on_immediate_wakeup_source_ready, + self, + (GDestroyNotify) clear_immediate_wakeup_source_pointer); + + g_source_attach (self->priv->immediate_wakeup_source, self->priv->context); + g_source_unref (source); + } + +-void +-goa_alarm_set_time (GoaAlarm *self, GDateTime *time, GCancellable *cancellable) ++static void ++goa_alarm_set_time (GoaAlarm *self, GDateTime *time) + { +- if (g_cancellable_is_cancelled (cancellable)) +- return; +- + g_rec_mutex_lock (&self->priv->lock); +- if (self->priv->cancellable != NULL && self->priv->cancellable != cancellable) +- g_cancellable_cancel (self->priv->cancellable); +- +- if (cancellable != NULL) +- g_object_ref (cancellable); +- +- if (self->priv->cancelled_id != 0) +- g_cancellable_disconnect (self->priv->cancellable, self->priv->cancelled_id); +- +- g_clear_object (&self->priv->cancellable); +- +- if (cancellable != NULL) +- self->priv->cancellable = cancellable; +- else +- self->priv->cancellable = g_cancellable_new (); +- +- self->priv->cancelled_id = g_cancellable_connect (self->priv->cancellable, +- G_CALLBACK (on_cancelled), +- self, NULL); + + g_date_time_ref (time); +- +- if (self->priv->time != NULL) +- g_date_time_unref (self->priv->time); +- + self->priv->time = time; + + if (self->priv->context == NULL) + self->priv->context = g_main_context_ref (g_main_context_default ()); + + schedule_wakeups (self); + + /* Wake up right away, in case it's already expired leaving the gate */ + schedule_immediate_wakeup (self); + g_rec_mutex_unlock (&self->priv->lock); + g_object_notify (G_OBJECT (self), "time"); + } + + GDateTime * + goa_alarm_get_time (GoaAlarm *self) + { + return self->priv->time; + } + + GoaAlarm * +-goa_alarm_new (void) ++goa_alarm_new (GDateTime *alarm_time) + { + GoaAlarm *self; + +- self = GOA_ALARM (g_object_new (GOA_TYPE_ALARM, NULL)); ++ self = GOA_ALARM (g_object_new (GOA_TYPE_ALARM, "time", alarm_time, NULL)); + + return GOA_ALARM (self); + } +diff --git a/src/goaidentity/goaalarm.h b/src/goaidentity/goaalarm.h +index a93991d..75252a4 100644 +--- a/src/goaidentity/goaalarm.h ++++ b/src/goaidentity/goaalarm.h +@@ -28,37 +28,34 @@ + #include + + G_BEGIN_DECLS + #define GOA_TYPE_ALARM (goa_alarm_get_type ()) + #define GOA_ALARM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOA_TYPE_ALARM, GoaAlarm)) + #define GOA_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GOA_TYPE_ALARM, GoaAlarmClass)) + #define GOA_IS_ALARM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOA_TYPE_ALARM)) + #define GOA_IS_ALARM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOA_TYPE_ALARM)) + #define GOA_ALARM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GOA_TYPE_ALARM, GoaAlarmClass)) + typedef struct _GoaAlarm GoaAlarm; + typedef struct _GoaAlarmClass GoaAlarmClass; + typedef struct _GoaAlarmPrivate GoaAlarmPrivate; + + struct _GoaAlarm + { + GObject parent; + + GoaAlarmPrivate *priv; + }; + + struct _GoaAlarmClass + { + GObjectClass parent_class; + + void (* fired) (GoaAlarm *alarm); + void (* rearmed) (GoaAlarm *alarm); + }; + + GType goa_alarm_get_type (void); + +-GoaAlarm *goa_alarm_new (void); +-void goa_alarm_set_time (GoaAlarm *alarm, +- GDateTime *time, +- GCancellable *cancellable); ++GoaAlarm *goa_alarm_new (GDateTime *time); + GDateTime *goa_alarm_get_time (GoaAlarm *alarm); + G_END_DECLS + #endif /* __GOA_ALARM_H__ */ +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index f06bf30..c3a65f6 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -29,121 +29,114 @@ + #include "goalogging.h" + + #include + #include + #include + + #include + #include + #include + + typedef enum + { + VERIFICATION_LEVEL_UNVERIFIED, + VERIFICATION_LEVEL_ERROR, + VERIFICATION_LEVEL_EXISTS, + VERIFICATION_LEVEL_SIGNED_IN + } VerificationLevel; + + struct _GoaKerberosIdentityPrivate + { + krb5_context kerberos_context; + krb5_ccache credentials_cache; + + char *identifier; + guint identifier_idle_id; + + krb5_timestamp expiration_time; + guint expiration_time_idle_id; + + GoaAlarm *expiration_alarm; +- GCancellable *expiration_alarm_cancellable; +- + GoaAlarm *expiring_alarm; +- GCancellable *expiring_alarm_cancellable; +- + GoaAlarm *renewal_alarm; +- GCancellable *renewal_alarm_cancellable; + + VerificationLevel cached_verification_level; + guint is_signed_in_idle_id; + }; + + enum + { + EXPIRING, + EXPIRED, + UNEXPIRED, + NEEDS_RENEWAL, + NEEDS_REFRESH, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_IDENTIFIER, + PROP_IS_SIGNED_IN, + 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_error_from_krb5_error_code (GoaKerberosIdentity *self, + GError **error, + gint code, + krb5_error_code error_code, + const char *format, + ...); + + 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 + goa_kerberos_identity_dispose (GObject *object) + { + GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (object); + + G_LOCK (identity_lock); +- clear_alarms (self); +- + g_clear_object (&self->priv->renewal_alarm); + g_clear_object (&self->priv->expiring_alarm); + g_clear_object (&self->priv->expiration_alarm); + 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->priv->identifier); + + if (self->priv->credentials_cache != NULL) + krb5_cc_close (self->priv->kerberos_context, self->priv->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); + +@@ -279,63 +272,60 @@ get_identifier (GoaKerberosIdentity *self, + principal, + 0, + &unparsed_name); + + if (error_code != 0) + { + const char *error_message; + + error_message = + krb5_get_error_message (self->priv->kerberos_context, error_code); + goa_debug ("GoaKerberosIdentity: Error parsing principal identity name: %s", + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + goto out; + } + + identifier = g_strdup (unparsed_name); + krb5_free_unparsed_name (self->priv->kerberos_context, unparsed_name); + + out: + krb5_free_principal (self->priv->kerberos_context, principal); + return identifier; + } + + static void + goa_kerberos_identity_init (GoaKerberosIdentity *self) + { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + GOA_TYPE_KERBEROS_IDENTITY, + GoaKerberosIdentityPrivate); +- self->priv->expiration_alarm = goa_alarm_new (); +- self->priv->expiring_alarm = goa_alarm_new (); +- self->priv->renewal_alarm = goa_alarm_new (); + } + + static void + set_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_message; + char *expanded_format; + va_list args; + char **chunks; + + error_message = krb5_get_error_message (self->priv->kerberos_context, error_code); + chunks = g_strsplit (format, "%k", -1); + expanded_format = g_strjoinv (error_message, chunks); + g_strfreev (chunks); + krb5_free_error_message (self->priv->kerberos_context, error_message); + + va_start (args, format); + literal_message = g_strdup_vprintf (expanded_format, args); + va_end (args); + + g_set_error_literal (error, GOA_IDENTITY_ERROR, code, literal_message); + g_free (literal_message); + } + +@@ -714,230 +704,233 @@ on_expiration_alarm_fired (GoaAlarm *alarm, + } + + static void + on_expiration_alarm_rearmed (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + + goa_debug ("GoaKerberosIdentity: expiration alarm rearmed"); + g_signal_emit (G_OBJECT (self), signals[NEEDS_REFRESH], 0); + } + + static void + on_renewal_alarm_rearmed (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + + goa_debug ("GoaKerberosIdentity: renewal alarm rearmed"); + } + + static void + on_renewal_alarm_fired (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + +- g_clear_object (&self->priv->renewal_alarm_cancellable); +- + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) + { + goa_debug ("GoaKerberosIdentity: renewal alarm fired for signed-in identity"); + g_signal_emit (G_OBJECT (self), signals[NEEDS_RENEWAL], 0); + } + } + + static void + on_expiring_alarm_rearmed (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + + goa_debug ("GoaKerberosIdentity: expiring alarm rearmed"); + } + + static void + on_expiring_alarm_fired (GoaAlarm *alarm, + GoaKerberosIdentity *self) + { + g_return_if_fail (GOA_IS_ALARM (alarm)); + g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self)); + +- g_clear_object (&self->priv->expiring_alarm_cancellable); +- + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN) + { + goa_debug ("GoaKerberosIdentity: expiring alarm fired for signed-in identity"); + g_signal_emit (G_OBJECT (self), signals[EXPIRING], 0); + } + } + ++static gboolean ++unref_alarm (GoaAlarm *alarm) ++{ ++ g_object_unref (G_OBJECT (alarm)); ++ return G_SOURCE_REMOVE; ++} ++ + static void +-set_alarm (GoaKerberosIdentity *self, +- GoaAlarm *alarm, +- GDateTime *alarm_time, +- GCancellable **cancellable) ++clear_alarm_and_unref_on_idle (GoaKerberosIdentity *self, ++ GoaAlarm **alarm) + { +- GDateTime *old_alarm_time; ++ if (!*alarm) ++ return; ++ ++ g_idle_add ((GSourceFunc) unref_alarm, *alarm); ++ *alarm = NULL; ++} ++ ++static void ++reset_alarm (GoaKerberosIdentity *self, ++ GoaAlarm **alarm, ++ GDateTime *alarm_time) ++{ ++ GDateTime *old_alarm_time = NULL; + + G_LOCK (identity_lock); +- old_alarm_time = goa_alarm_get_time (alarm); ++ if (*alarm) ++ old_alarm_time = goa_alarm_get_time (*alarm); + if (old_alarm_time == NULL || !g_date_time_equal (alarm_time, old_alarm_time)) + { +- GCancellable *new_cancellable; +- +- new_cancellable = g_cancellable_new (); +- goa_alarm_set_time (alarm, alarm_time, new_cancellable); +- +- g_clear_object (cancellable); +- *cancellable = new_cancellable; ++ clear_alarm_and_unref_on_idle (self, alarm); ++ *alarm = goa_alarm_new (alarm_time); + } + G_UNLOCK (identity_lock); + + } + + static void + disconnect_alarm_signals (GoaKerberosIdentity *self) + { +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), +- G_CALLBACK (on_renewal_alarm_fired), +- self); +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), +- G_CALLBACK (on_renewal_alarm_rearmed), +- self); +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), +- G_CALLBACK (on_expiring_alarm_fired), +- self); +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), +- G_CALLBACK (on_expiration_alarm_rearmed), +- self); +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), +- G_CALLBACK (on_expiration_alarm_fired), +- self); +- g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), +- G_CALLBACK (on_expiring_alarm_rearmed), +- self); ++ if (self->priv->renewal_alarm) ++ { ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), ++ G_CALLBACK (on_renewal_alarm_fired), ++ self); ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->renewal_alarm), ++ G_CALLBACK (on_renewal_alarm_rearmed), ++ self); ++ } ++ ++ if (self->priv->expiring_alarm) ++ { ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), ++ G_CALLBACK (on_expiring_alarm_fired), ++ self); ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiring_alarm), ++ G_CALLBACK (on_expiring_alarm_rearmed), ++ self); ++ } ++ ++ if (self->priv->expiration_alarm) ++ { ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), ++ G_CALLBACK (on_expiration_alarm_rearmed), ++ self); ++ g_signal_handlers_disconnect_by_func (G_OBJECT (self->priv->expiration_alarm), ++ G_CALLBACK (on_expiration_alarm_fired), ++ self); ++ } + } + + static void + connect_alarm_signals (GoaKerberosIdentity *self) + { + g_signal_connect (G_OBJECT (self->priv->renewal_alarm), + "fired", + G_CALLBACK (on_renewal_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->renewal_alarm), + "rearmed", + G_CALLBACK (on_renewal_alarm_rearmed), + self); + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "fired", + G_CALLBACK (on_expiring_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiring_alarm), + "rearmed", + G_CALLBACK (on_expiring_alarm_rearmed), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "fired", + G_CALLBACK (on_expiration_alarm_fired), + self); + g_signal_connect (G_OBJECT (self->priv->expiration_alarm), + "rearmed", + G_CALLBACK (on_expiration_alarm_rearmed), + self); + } + + static void + reset_alarms (GoaKerberosIdentity *self) + { + GDateTime *now; + GDateTime *expiration_time; + GDateTime *expiring_time; + GDateTime *renewal_time; + GTimeSpan time_span_until_expiration; + + now = g_date_time_new_now_local (); + expiration_time = g_date_time_new_from_unix_local (self->priv->expiration_time); + time_span_until_expiration = g_date_time_difference (expiration_time, now); + g_date_time_unref (now); + + /* Let the user reauthenticate 10 min before expiration */ + expiring_time = g_date_time_add_minutes (expiration_time, -10); + + /* Try to quietly auto-renew halfway through so in ideal configurations + * the ticket is never more than halfway to expired + */ + renewal_time = g_date_time_add (expiration_time, + -(time_span_until_expiration / 2)); + + disconnect_alarm_signals (self); + +- set_alarm (self, +- self->priv->renewal_alarm, +- renewal_time, &self->priv->renewal_alarm_cancellable); +- set_alarm (self, +- self->priv->expiring_alarm, +- expiring_time, &self->priv->expiring_alarm_cancellable); +- set_alarm (self, +- self->priv->expiration_alarm, +- expiration_time, &self->priv->expiration_alarm_cancellable); ++ reset_alarm (self, &self->priv->renewal_alarm, renewal_time); ++ reset_alarm (self, &self->priv->expiring_alarm, expiring_time); ++ reset_alarm (self, &self->priv->expiration_alarm, expiration_time); + + g_date_time_unref (renewal_time); + g_date_time_unref (expiring_time); + g_date_time_unref (expiration_time); + connect_alarm_signals (self); + } + + static void +-cancel_and_clear_cancellable (GCancellable **cancellable) +-{ +- if (cancellable == NULL) +- return; +- +- if (!g_cancellable_is_cancelled (*cancellable)) +- g_cancellable_cancel (*cancellable); +- +- g_clear_object (cancellable); +-} +- +-static void + clear_alarms (GoaKerberosIdentity *self) + { +- cancel_and_clear_cancellable (&self->priv->renewal_alarm_cancellable); +- cancel_and_clear_cancellable (&self->priv->expiring_alarm_cancellable); +- cancel_and_clear_cancellable (&self->priv->expiration_alarm_cancellable); ++ disconnect_alarm_signals (self); ++ clear_alarm_and_unref_on_idle (self, &self->priv->renewal_alarm); ++ clear_alarm_and_unref_on_idle (self, &self->priv->expiring_alarm); ++ clear_alarm_and_unref_on_idle (self, &self->priv->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->priv->identifier == NULL) + { + self->priv->identifier = get_identifier (self, error); + + if (self->priv->identifier != NULL) + queue_notify (self, &self->priv->identifier_idle_id, "identifier"); + } + + verification_error = NULL; + self->priv->cached_verification_level = + verify_identity (self, &verification_error); + + switch (self->priv->cached_verification_level) + { + case VERIFICATION_LEVEL_EXISTS: + case VERIFICATION_LEVEL_SIGNED_IN: + reset_alarms (self); +-- +1.8.3.1 + + +From cf3bffb0e0a5eb2e5e37709956de70a48f12eb83 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Fri, 9 May 2014 09:11:13 -0400 +Subject: [PATCH 23/24] goaidentity: don't leak credentials caches + +krb5_cc_default doesn't return a shared resource, and +the results need to be freed. Likewise, +get_new_credentials_cache needs to be freed. + +https://bugzilla.gnome.org/show_bug.cgi?id=729874 +--- + src/goaidentity/goakerberosidentitymanager.c | 33 ++++++++++++---------------- + 1 file changed, 14 insertions(+), 19 deletions(-) + +diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c +index 5e33003..7cc90b9 100644 +--- a/src/goaidentity/goakerberosidentitymanager.c ++++ b/src/goaidentity/goakerberosidentitymanager.c +@@ -767,127 +767,120 @@ get_new_credentials_cache (GoaKerberosIdentityManager *self, + + if (g_strcmp0 (self->priv->credentials_cache_type, "FILE") == 0) + { + goa_debug ("GoaKerberosIdentityManager: credential cache type %s doesn't supports cache collections", self->priv->credentials_cache_type); + supports_multiple_identities = FALSE; + } + else if (g_strcmp0 (self->priv->credentials_cache_type, "DIR") == 0 || + g_strcmp0 (self->priv->credentials_cache_type, "KEYRING") == 0) + { + goa_debug ("GoaKerberosIdentityManager: credential cache type %s supports cache collections", self->priv->credentials_cache_type); + supports_multiple_identities = TRUE; + } + else + { + goa_debug ("GoaKerberosIdentityManager: don't know if credential cache type %s supports cache collections, assuming yes", self->priv->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->priv->identities) == 0) + { +- krb5_ccache default_cache; +- +- error_code = krb5_cc_default (self->priv->kerberos_context, &default_cache); +- +- if (error_code == 0) +- krb5_cc_dup (self->priv->kerberos_context, default_cache, credentials_cache); ++ error_code = krb5_cc_default (self->priv->kerberos_context, credentials_cache); + } + else + { + error_code = krb5_cc_new_unique (self->priv->kerberos_context, + self->priv->credentials_cache_type, + NULL, + credentials_cache); + } + + return error_code; + } + + static void + sign_in_identity (GoaKerberosIdentityManager *self, + Operation *operation) + { + GoaIdentity *identity; + GError *error; + krb5_error_code error_code; + + goa_debug ("GoaKerberosIdentityManager: signing in identity %s", + operation->identifier); + error = NULL; + identity = g_hash_table_lookup (self->priv->identities, operation->identifier); + if (identity == NULL) + { + krb5_ccache credentials_cache; + + error_code = get_new_credentials_cache (self, &credentials_cache); + + if (error_code != 0) + { + const char *error_message; + + error_message = + krb5_get_error_message (self->priv->kerberos_context, error_code); + goa_debug ("GoaKerberosIdentityManager: Error creating new cache for identity credentials: %s", + error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + + g_simple_async_result_set_error (operation->result, + GOA_IDENTITY_MANAGER_ERROR, + GOA_IDENTITY_MANAGER_ERROR_CREATING_IDENTITY, + _("Could not create credential cache for identity")); + g_simple_async_result_set_op_res_gpointer (operation->result, NULL, NULL); + return; + } +- else ++ ++ identity = goa_kerberos_identity_new (self->priv->kerberos_context, ++ credentials_cache, ++ &error); ++ krb5_cc_close (self->priv->kerberos_context, credentials_cache); ++ if (identity == NULL) + { +- identity = goa_kerberos_identity_new (self->priv->kerberos_context, +- credentials_cache, +- &error); +- if (identity == NULL) +- { +- krb5_cc_close (self->priv->kerberos_context, credentials_cache); +- g_simple_async_result_take_error (operation->result, error); +- g_simple_async_result_set_op_res_gpointer (operation->result, +- NULL, +- NULL); +- return; +- } ++ g_simple_async_result_take_error (operation->result, error); ++ g_simple_async_result_set_op_res_gpointer (operation->result, ++ NULL, ++ NULL); ++ return; + } + } + else + { + g_object_ref (identity); + } + + g_hash_table_replace (self->priv->identities, + g_strdup (operation->identifier), + g_object_ref (identity)); + + if (!goa_kerberos_identity_sign_in (GOA_KERBEROS_IDENTITY (identity), + operation->identifier, + operation->initial_password, + operation->sign_in_flags, + (GoaIdentityInquiryFunc) + on_kerberos_identity_inquiry, + operation, + NULL, + operation->cancellable, + &error)) + { + g_simple_async_result_set_from_error (operation->result, error); + g_simple_async_result_set_op_res_gpointer (operation->result, + NULL, + NULL); + + } + else + { +@@ -1440,60 +1433,62 @@ monitor_credentials_cache (GoaKerberosIdentityManager *self, + &monitoring_error); + g_object_unref (directory); + + } + g_object_unref (file); + } + + if (monitor == NULL) + { + if (monitoring_error != NULL) + { + goa_warning ("GoaKerberosIdentityManager: Could not monitor credentials for %s (type %s), reverting to polling: %s", + cache_path, + cache_type, + monitoring_error != NULL? monitoring_error->message : ""); + g_clear_error (&monitoring_error); + } + can_monitor = FALSE; + } + else + { + self->priv->credentials_cache_changed_signal_id = + g_signal_connect (G_OBJECT (monitor), "changed", + G_CALLBACK (on_credentials_cache_changed), self); + self->priv->credentials_cache_monitor = monitor; + } + + if (!can_monitor) + self->priv->polling_timeout_id = g_timeout_add_seconds (FALLBACK_POLLING_INTERVAL, (GSourceFunc) on_polling_timeout, self); + ++ krb5_cc_close (self->priv->kerberos_context, default_cache); ++ + return TRUE; + } + + static void + stop_watching_credentials_cache (GoaKerberosIdentityManager *self) + { + if (self->priv->credentials_cache_monitor != NULL) + { + if (!g_file_monitor_is_cancelled (self->priv->credentials_cache_monitor)) + g_file_monitor_cancel (self->priv->credentials_cache_monitor); + + g_clear_object (&self->priv->credentials_cache_monitor); + } + + if (self->priv->polling_timeout_id != 0) + { + g_source_remove (self->priv->polling_timeout_id); + self->priv->polling_timeout_id = 0; + } + } + + static gboolean + goa_kerberos_identity_manager_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) + { + GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (initable); + krb5_error_code error_code; + GError *monitoring_error; + +-- +1.8.3.1 + +From 097aa719985923fd551c2c68a63ebca3609031ca Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 6 Nov 2013 16:58:43 -0500 +Subject: [PATCH 24/24] kerberos: don't crash if keyring credentials disappear + +It's possible to make gnome-online-accounts crash by: + +1) creating a kerberos account in control-center +2) deleting the stored credentials from gnome keyring using seahorse +3) running kdestroy in the terminal to make the sign in button show up +in the control-center panel +4) clicking sign in + +This is because the provider makes the assumption that there will always +be stored credentials when signing in interactively, which is incorrect +in the above scenario (because of step 2). + +This commit hardens the get_ticket_sync function against credentials +disappearing. + +https://bugzilla.gnome.org/show_bug.cgi?id=711572 +--- + src/goabackend/goakerberosprovider.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c +index e795856..fbe0364 100644 +--- a/src/goabackend/goakerberosprovider.c ++++ b/src/goabackend/goakerberosprovider.c +@@ -668,97 +668,101 @@ look_up_identity (GoaKerberosProvider *self, + operation_result); + } + + static void + on_account_signed_in (GoaProvider *provider, + GAsyncResult *result, + SignInRequest *request) + { + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + &request->error)) + { + g_main_loop_quit (request->loop); + return; + } + + g_main_loop_quit (request->loop); + } + + static gboolean + get_ticket_sync (GoaKerberosProvider *self, + GoaObject *object, + gboolean is_interactive, + GCancellable *cancellable, + GError **error) + { + GVariant *credentials; + GError *lookup_error; + GoaAccount *account; + const char *identifier; + const char *password; +- gboolean has_password; + SignInRequest request; + gboolean ret; + + ret = FALSE; + + account = goa_object_peek_account (object); + identifier = goa_account_get_identity (account); + password = NULL; + + lookup_error = NULL; + credentials = goa_utils_lookup_credentials_sync (GOA_PROVIDER (self), + object, + cancellable, + &lookup_error); + + if (credentials == NULL && !is_interactive) + { + if (lookup_error != NULL) + g_propagate_error (error, lookup_error); + else + g_set_error (error, + GOA_ERROR, + GOA_ERROR_NOT_AUTHORIZED, + _("Could not find saved credentials for principal `%s' in keyring"), identifier); + goto out; + } +- +- has_password = g_variant_lookup (credentials, "password", "&s", &password); +- if (!has_password && !is_interactive) ++ else if (credentials != NULL) + { +- g_set_error (error, +- GOA_ERROR, +- GOA_ERROR_NOT_AUTHORIZED, +- _("Did not find password for principal `%s' in credentials"), +- identifier); +- goto out; ++ gboolean has_password; ++ ++ has_password = g_variant_lookup (credentials, "password", "&s", &password); ++ ++ if (!has_password && !is_interactive) ++ { ++ g_set_error (error, ++ GOA_ERROR, ++ GOA_ERROR_NOT_AUTHORIZED, ++ _("Did not find password for principal `%s' in credentials"), ++ identifier); ++ goto out; ++ } + } + + memset (&request, 0, sizeof (SignInRequest)); + request.loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE); + request.error = NULL; + + sign_in_identity (self, + identifier, + password, + cancellable, + (GAsyncReadyCallback) + on_account_signed_in, + &request); + + g_main_loop_run (request.loop); + g_main_loop_unref (request.loop); + + if (request.error != NULL) + { + g_propagate_error (error, request.error); + goto out; + } + + ret = TRUE; + out: + if (credentials != NULL) + g_variant_unref (credentials); + + return ret; + } +-- +1.8.3.1 + diff --git a/SPECS/gnome-online-accounts.spec b/SPECS/gnome-online-accounts.spec index d82d59a..01fb218 100644 --- a/SPECS/gnome-online-accounts.spec +++ b/SPECS/gnome-online-accounts.spec @@ -1,6 +1,6 @@ Name: gnome-online-accounts Version: 3.8.5 -Release: 8%{?dist} +Release: 9%{?dist}.1 Summary: Provide online accounts information Group: System Environment/Libraries @@ -23,7 +23,7 @@ BuildRequires: rest-devel BuildRequires: libxml2-devel Requires: realmd -Patch0: fix-kerberos-crash.patch +Patch0: kerberos-fixes.patch Patch1: translations.patch Patch2: google-drop-password-based.patch Patch3: facebook-updates-and-fixes.patch @@ -45,7 +45,7 @@ files for developing applications that use gnome-online-accounts. %prep %setup -q -%patch0 -p1 -b .fix-kerberos-crash +%patch0 -p1 -b .kerberos-fixes %patch1 -p2 -b .translations %patch2 -p1 -b .google-drop-password-based %patch3 -p1 -b .facebook-updates-and-fixes @@ -109,6 +109,14 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_libdir}/goa-1.0/include %changelog +* Fri May 23 2014 Ray Strode 3.8.5-9.1 +- Bump for dist tag confusion + Related: #1096399 + +* Fri May 09 2014 Ray Strode 3.8.5-8.1 +- Fix various resource leaks in the kerberos code + Resolves: #1096399 + * Mon Mar 31 2014 Debarshi Ray - 3.8.5-8 - Popup is too small to display Facebook authorization Resolves: #1081520