Blob Blame History Raw
From 0c9359604da3091c53a152cebe0e21861d2fceee Mon Sep 17 00:00:00 2001
From: Debarshi Ray <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <rstrode@redhat.com>
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 <debarshir@gnome.org>
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 <mike@cchtml.com>
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 <mike@cchtml.com>
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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <mike@cchtml.com>
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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <glib/gi18n-lib.h>
 #include <gmodule.h>
 
 #include <glib.h>
 #include <glib-object.h>
 #include <gio/gio.h>
 
 #include <gcr/gcr.h>
 
 #include <goa/goa.h>
 
 #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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <rstrode@redhat.com>
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 <rstrode@redhat.com>
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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <sys/timerfd.h>
 #endif
 
 #include <unistd.h>
 #include <string.h>
 
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <gio/gunixinputstream.h>
 
 #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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <debarshir@gnome.org>
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 <rstrode@redhat.com>
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 <sys/timerfd.h>
 #endif
 
 #include <unistd.h>
 #include <string.h>
 
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <gio/gunixinputstream.h>
 
 #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 <gio/gio.h>
 
 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 <netinet/in.h>
 #include <arpa/nameser.h>
 #include <resolv.h>
 
 #include <string.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
 
 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 <rstrode@redhat.com>
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 <rstrode@redhat.com>
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