Blob Blame History Raw
From 6a71ae5ca9254b1072faeffbc5ad943e6204a340 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Thu, 30 Oct 2014 14:12:08 -0400
Subject: [PATCH 01/10] identity: destroy alarms from main thread, even on
 dispose

These days we defer destroying alarms to the main thread when
alarms are reset.  We still dispose of alarms in the current
thread when disposing their identity object, however.

This commit changes the code to always dispose of alarms from
the main thread.
---
 src/goaidentity/goakerberosidentity.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index 7641102..e346fe7 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -85,63 +85,61 @@ enum
 
 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);
-  g_clear_object (&self->priv->renewal_alarm);
-  g_clear_object (&self->priv->expiring_alarm);
-  g_clear_object (&self->priv->expiration_alarm);
+  clear_alarms (self);
   g_clear_pointer (&self->priv->preauth_identity_source,
                    g_free);
   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);
 
   switch (property_id)
-- 
1.8.3.1


From 4ee666fc82819ffafb4ec349f5471149a0078739 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 28 Oct 2014 16:39:23 -0400
Subject: [PATCH 02/10] identity: add some locking around expiration time

We don't want the expiration time to get read while it's getting
written.
---
 src/goaidentity/goakerberosidentity.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index e346fe7..ab8d9c0 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -553,79 +553,88 @@ on_notify_queued (NotifyRequest *request)
 
 static void
 queue_notify (GoaKerberosIdentity *self,
               guint               *idle_id,
               const char          *property_name)
 {
   NotifyRequest *request;
 
   if (*idle_id != 0)
     {
       return;
     }
 
   request = g_slice_new0 (NotifyRequest);
   request->self = g_object_ref (self);
   request->idle_id = idle_id;
   request->property_name = property_name;
 
   *idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
                               (GSourceFunc)
                               on_notify_queued,
                               request,
                               (GDestroyNotify)
                               clear_idle_id);
 }
 
 static void
 set_expiration_time (GoaKerberosIdentity *self,
                      krb5_timestamp       expiration_time)
 {
+  G_LOCK (identity_lock);
   if (self->priv->expiration_time != expiration_time)
     {
       self->priv->expiration_time = expiration_time;
+      G_UNLOCK (identity_lock);
       queue_notify (self,
                     &self->priv->expiration_time_idle_id,
                     "expiration-timestamp");
+      G_LOCK (identity_lock);
     }
+  G_UNLOCK (identity_lock);
 }
 
 static gboolean
 credentials_are_expired (GoaKerberosIdentity *self,
                          krb5_creds          *credentials)
 {
   krb5_timestamp current_time;
+  krb5_timestamp expiration_time;
 
   current_time = get_current_time (self);
 
-  set_expiration_time (self, MAX (credentials->times.endtime,
-                                  self->priv->expiration_time));
+  G_LOCK (identity_lock);
+  expiration_time = MAX (credentials->times.endtime,
+                         self->priv->expiration_time);
+  G_UNLOCK (identity_lock);
+
+  set_expiration_time (self, expiration_time);
 
   if (credentials->times.endtime <= current_time)
     {
       return TRUE;
     }
 
   return FALSE;
 }
 
 static VerificationLevel
 verify_identity (GoaKerberosIdentity  *self,
                  char                **preauth_identity_source,
                  GError              **error)
 {
   krb5_principal principal;
   krb5_cc_cursor cursor;
   krb5_creds credentials;
   krb5_error_code error_code;
   VerificationLevel verification_level;
 
   set_expiration_time (self, 0);
 
   if (self->priv->credentials_cache == NULL)
     return VERIFICATION_LEVEL_UNVERIFIED;
 
   error_code = krb5_cc_get_principal (self->priv->kerberos_context,
                                       self->priv->credentials_cache,
                                       &principal);
 
   if (error_code != 0)
@@ -896,61 +905,63 @@ connect_alarm_signals (GoaKerberosIdentity *self)
                     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 ();
+  G_LOCK (identity_lock);
   expiration_time = g_date_time_new_from_unix_local (self->priv->expiration_time);
+  G_UNLOCK (identity_lock);
   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);
 
   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
 clear_alarms (GoaKerberosIdentity *self)
 {
   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);
-- 
1.8.3.1


From 2b8d542b56a45a7bd4feb1337ebb163da9bb15dc Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 29 Oct 2014 16:10:32 -0400
Subject: [PATCH 03/10] goadaemon: fix mem leak

---
 src/daemon/goadaemon.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/daemon/goadaemon.c b/src/daemon/goadaemon.c
index 2fb6173..1a6ac65 100644
--- a/src/daemon/goadaemon.c
+++ b/src/daemon/goadaemon.c
@@ -485,60 +485,61 @@ update_account_object (GoaDaemon           *daemon,
   serialized_icon = g_icon_to_string (icon);
 
   goa_account_set_id (account, g_strrstr (g_dbus_object_get_object_path (G_DBUS_OBJECT (object)), "/") + 1);
   goa_account_set_provider_type (account, type);
   goa_account_set_provider_name (account, name);
   goa_account_set_provider_icon (account, serialized_icon);
   goa_account_set_identity (account, identity);
   goa_account_set_presentation_identity (account, presentation_identity);
 
   error = NULL;
   if (!goa_provider_build_object (provider, object, key_file, group, daemon->connection, just_added, &error))
     {
       goa_warning ("Error parsing account: %s (%s, %d)",
                    error->message, g_quark_to_string (error->domain), error->code);
       g_error_free (error);
       goto out;
     }
 
   ret = TRUE;
 
  out:
   g_free (serialized_icon);
   if (icon != NULL)
     g_object_unref (icon);
   g_free (name);
   if (provider != NULL)
     g_object_unref (provider);
   g_object_unref (account);
   g_free (type);
   g_free (identity);
+  g_free (presentation_identity);
   return ret;
 }
 
 static gchar *
 object_path_to_group (const gchar *object_path)
 {
   g_return_val_if_fail (g_str_has_prefix (object_path, "/org/gnome/OnlineAccounts/Accounts/"), NULL);
   return g_strdup_printf ("Account %s", object_path + sizeof "/org/gnome/OnlineAccounts/Accounts/" - 1);
 }
 
 static void
 process_config_entries (GoaDaemon  *daemon,
                         GHashTable *group_name_to_key_file_data)
 {
   GHashTableIter iter;
   const gchar *id;
   KeyFileData *key_file_data;
   GList *existing_object_paths;
   GList *config_object_paths;
   GList *added;
   GList *removed;
   GList *unchanged;
   GList *l;
 
   existing_object_paths = NULL;
   {
     GList *existing_objects;
     existing_objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (daemon->object_manager));
     for (l = existing_objects; l != NULL; l = l->next)
       {
-- 
1.8.3.1


From 6536547a53a482c19f2446c0cebc7cd0ccb65085 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 29 Oct 2014 16:11:38 -0400
Subject: [PATCH 04/10] logging: fix leak

---
 src/goabackend/goalogging.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/goabackend/goalogging.c b/src/goabackend/goalogging.c
index 8301884..cf195cf 100644
--- a/src/goabackend/goalogging.c
+++ b/src/goabackend/goalogging.c
@@ -197,33 +197,34 @@ goa_log (GoaLogLevel     level,
       syslog_priority = LOG_WARNING;
       level_color_str = _color_get (_COLOR_FG_YELLOW);
       break;
 
     case GOA_LOG_LEVEL_ERROR:
       level_str = "[ERROR]";
       syslog_priority = LOG_ERR;
       level_color_str = _color_get (_COLOR_FG_RED);
       break;
 
     default:
       g_assert_not_reached ();
       break;
     }
 
  /* TODO: Need to find a portable way of getting the thread ID (#660177) */
 #ifdef SYS_gettid
   thread_str = g_strdup_printf ("%d", (gint) syscall (SYS_gettid));
 #else
   thread_str = g_strdup_printf ("%d", (gint) getpid());
 #endif /* SYS_gettid */
   g_print ("%s%s%s.%03d:%s%s%s[%s]%s:%s%s%s:%s %s %s[%s, %s()]%s\n",
            _color_get (_COLOR_BOLD_ON), _color_get (_COLOR_FG_YELLOW), time_buf, (gint) now.tv_usec / 1000, _color_get (_COLOR_RESET),
            _color_get (_COLOR_FG_MAGENTA), _color_get (_COLOR_BOLD_ON), thread_str, _color_get (_COLOR_RESET),
            level_color_str, _color_get (_COLOR_BOLD_ON), level_str, _color_get (_COLOR_RESET),
            message,
            _color_get (_COLOR_FG_BLACK), location, function, _color_get (_COLOR_RESET));
   if (level >= GOA_LOG_LEVEL_NOTICE)
     syslog (syslog_priority, "%s [%s, %s()]", message, location, function);
   g_free (message);
+  g_free (thread_str);
 
   G_UNLOCK (log_lock);
 }
-- 
1.8.3.1


From f1a65c7fbda987854790ea564e1e7ec4da46762a Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 29 Oct 2014 16:12:31 -0400
Subject: [PATCH 05/10] identity: plug leak

---
 src/goaidentity/goaidentityservice.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c
index b570e09..883f549 100644
--- a/src/goaidentity/goaidentityservice.c
+++ b/src/goaidentity/goaidentityservice.c
@@ -1594,71 +1594,74 @@ on_identities_listed (GoaIdentityManager *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);
         }
       goto out;
     }
 
   for (node = identities; node != NULL; node = node->next)
     {
       GoaIdentity *identity = node->data;
       const char  *principal;
       GoaObject   *object;
+      char *object_path;
 
-      export_identity (self, identity);
+      object_path = 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);
+
+      g_free (object_path);
     }
 
  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;
-- 
1.8.3.1


From 2ec6566056331be0a536a19ed738e5b5a1cef020 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Wed, 29 Oct 2014 16:13:11 -0400
Subject: [PATCH 06/10] kerberos: plug leak

---
 src/goaidentity/goakerberosidentity.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index ab8d9c0..cb9ca00 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -299,60 +299,61 @@ static void
 goa_kerberos_identity_init (GoaKerberosIdentity *self)
 {
   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
                                             GOA_TYPE_KERBEROS_IDENTITY,
                                             GoaKerberosIdentityPrivate);
 }
 
 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_free (expanded_format);
 
   g_set_error_literal (error, GOA_IDENTITY_ERROR, code, literal_message);
   g_free (literal_message);
 }
 
 char *
 goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self)
 {
   krb5_principal principal;
   krb5_error_code error_code;
   char *unparsed_name;
   char *principal_name;
   int flags;
 
   if (self->priv->identifier == NULL)
     return NULL;
 
   error_code = krb5_parse_name (self->priv->kerberos_context,
                                 self->priv->identifier,
                                 &principal);
 
   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 identity %s into kerberos principal: %s",
          self->priv->identifier, error_message);
       krb5_free_error_message (self->priv->kerberos_context, error_message);
-- 
1.8.3.1


From ce5603a3180a9736c3b54ce46ed29527d9a818cc Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 28 Oct 2014 16:45:06 -0400
Subject: [PATCH 07/10] identity: make verify_identity have one exit path

This makes the control flow easier to follow and lays the way
for a follow up change.
---
 src/goaidentity/goakerberosidentity.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index cb9ca00..e46f684 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -596,87 +596,88 @@ set_expiration_time (GoaKerberosIdentity *self,
 
 static gboolean
 credentials_are_expired (GoaKerberosIdentity *self,
                          krb5_creds          *credentials)
 {
   krb5_timestamp current_time;
   krb5_timestamp expiration_time;
 
   current_time = get_current_time (self);
 
   G_LOCK (identity_lock);
   expiration_time = MAX (credentials->times.endtime,
                          self->priv->expiration_time);
   G_UNLOCK (identity_lock);
 
   set_expiration_time (self, expiration_time);
 
   if (credentials->times.endtime <= current_time)
     {
       return TRUE;
     }
 
   return FALSE;
 }
 
 static VerificationLevel
 verify_identity (GoaKerberosIdentity  *self,
                  char                **preauth_identity_source,
                  GError              **error)
 {
-  krb5_principal principal;
+  krb5_principal principal = NULL;
   krb5_cc_cursor cursor;
   krb5_creds credentials;
   krb5_error_code error_code;
-  VerificationLevel verification_level;
+  VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED;
 
   set_expiration_time (self, 0);
 
   if (self->priv->credentials_cache == NULL)
-    return VERIFICATION_LEVEL_UNVERIFIED;
+    goto out;
 
   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 || error_code == KRB5_FCC_NOFILE)
-        return VERIFICATION_LEVEL_UNVERIFIED;
+        goto out;
 
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_NOT_FOUND,
                                       error_code,
                                       _("Could not find identity in "
                                         "credential cache: %k"));
-      return VERIFICATION_LEVEL_ERROR;
+      verification_level = VERIFICATION_LEVEL_ERROR;
+      goto out;
     }
 
   error_code = krb5_cc_start_seq_get (self->priv->kerberos_context,
                                       self->priv->credentials_cache, &cursor);
   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;
@@ -700,61 +701,62 @@ verify_identity (GoaKerberosIdentity  *self,
     {
       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 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);
+  if (principal != NULL)
+    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;
   G_UNLOCK (identity_lock);
 
   return is_signed_in;
 }
 
 static void
 identity_interface_init (GoaIdentityInterface *interface)
 {
   interface->get_identifier = goa_kerberos_identity_get_identifier;
   interface->is_signed_in = goa_kerberos_identity_is_signed_in;
 }
 
 static void
 on_expiration_alarm_fired (GoaAlarm            *alarm,
                            GoaKerberosIdentity *self)
 {
   g_return_if_fail (GOA_IS_ALARM (alarm));
   g_return_if_fail (GOA_IS_KERBEROS_IDENTITY (self));
-- 
1.8.3.1


From e084fddbebf74f994470233c98a4fde51f55d6a8 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 28 Oct 2014 16:54:06 -0400
Subject: [PATCH 08/10] identity: don't set expiration_time to 0 intermediately
 while verifying identity

The top of verify_identity calls

set_expiration_time(self, 0);

as a way to make sure the expiration time is 0'd if the identity's
credentials are invalidated.  In the case, they are still valid,
set_expiration_time is called again with the new, most up to date
value. The problem is the intermediate 0 isn't invisible, there's a
window where the identity can be read and 0 will get returned, even
when the identity is valid.

This commit defers setting the expiration time until the very end of
the function.
---
 src/goaidentity/goakerberosidentity.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c
index e46f684..c776d04 100644
--- a/src/goaidentity/goakerberosidentity.c
+++ b/src/goaidentity/goakerberosidentity.c
@@ -569,192 +569,191 @@ queue_notify (GoaKerberosIdentity *self,
   request->idle_id = idle_id;
   request->property_name = property_name;
 
   *idle_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
                               (GSourceFunc)
                               on_notify_queued,
                               request,
                               (GDestroyNotify)
                               clear_idle_id);
 }
 
 static void
 set_expiration_time (GoaKerberosIdentity *self,
                      krb5_timestamp       expiration_time)
 {
   G_LOCK (identity_lock);
   if (self->priv->expiration_time != expiration_time)
     {
       self->priv->expiration_time = expiration_time;
       G_UNLOCK (identity_lock);
       queue_notify (self,
                     &self->priv->expiration_time_idle_id,
                     "expiration-timestamp");
       G_LOCK (identity_lock);
     }
   G_UNLOCK (identity_lock);
 }
 
 static gboolean
 credentials_are_expired (GoaKerberosIdentity *self,
-                         krb5_creds          *credentials)
+                         krb5_creds          *credentials,
+                         krb5_timestamp      *expiration_time)
 {
   krb5_timestamp current_time;
-  krb5_timestamp expiration_time;
 
   current_time = get_current_time (self);
 
   G_LOCK (identity_lock);
-  expiration_time = MAX (credentials->times.endtime,
-                         self->priv->expiration_time);
+  *expiration_time = MAX (credentials->times.endtime,
+                          self->priv->expiration_time);
   G_UNLOCK (identity_lock);
 
-  set_expiration_time (self, expiration_time);
-
   if (credentials->times.endtime <= current_time)
     {
       return TRUE;
     }
 
   return FALSE;
 }
 
 static VerificationLevel
 verify_identity (GoaKerberosIdentity  *self,
                  char                **preauth_identity_source,
                  GError              **error)
 {
   krb5_principal principal = NULL;
   krb5_cc_cursor cursor;
   krb5_creds credentials;
   krb5_error_code error_code;
+  krb5_timestamp expiration_time = 0;
   VerificationLevel verification_level = VERIFICATION_LEVEL_UNVERIFIED;
 
-  set_expiration_time (self, 0);
-
   if (self->priv->credentials_cache == NULL)
     goto out;
 
   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 || error_code == KRB5_FCC_NOFILE)
         goto out;
 
       set_error_from_krb5_error_code (self,
                                       error,
                                       GOA_IDENTITY_ERROR_NOT_FOUND,
                                       error_code,
                                       _("Could not find identity in "
                                         "credential cache: %k"));
       verification_level = VERIFICATION_LEVEL_ERROR;
       goto out;
     }
 
   error_code = krb5_cc_start_seq_get (self->priv->kerberos_context,
                                       self->priv->credentials_cache, &cursor);
   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))
+          if (!credentials_are_expired (self, &credentials, &expiration_time))
             verification_level = VERIFICATION_LEVEL_SIGNED_IN;
           else
             verification_level = VERIFICATION_LEVEL_EXISTS;
         }
       else
         {
           snoop_preauth_identity_from_credentials (self, &credentials, preauth_identity_source);
         }
 
       krb5_free_cred_contents (self->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 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:
+  set_expiration_time (self, expiration_time);
+
   if (principal != NULL)
     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;
   G_UNLOCK (identity_lock);
 
   return is_signed_in;
 }
 
 static void
 identity_interface_init (GoaIdentityInterface *interface)
 {
   interface->get_identifier = goa_kerberos_identity_get_identifier;
   interface->is_signed_in = goa_kerberos_identity_is_signed_in;
 }
 
 static void
 on_expiration_alarm_fired (GoaAlarm            *alarm,
                            GoaKerberosIdentity *self)
 {
-- 
1.8.3.1


From 530786dbf442b3ef4c04b47da42d5b8f8e7d56b3 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 28 Oct 2014 17:16:18 -0400
Subject: [PATCH 09/10] kerberos: maintain one long-lasting object manager
 client to kerberos service

At the moment, the kerberos backend creates one object manager to the
kerberos identity service per provider object.  Provider objects are actually
fairly transient and get created and destroyed, in some cases, per operation
on an account. The upshot is, object manager clients end up getting created
more frequently than they really should be. To make matters worse, the kerberos
provider has no finalize function, so these object manager clients are getting
leaked.

This commit makes the object manager client get created once at start up,
and get reused by all providers.  Since there's only one object manager,
rooted in the main thread, using the main thread's main loop context
now, the per-thread synchronous codepaths can't call object manager async
functions using a local main loop context. They do this, at the moment, because
there are async, main thread code paths that also need to talk to the
kerberos service. The local main loop context provides a way to call the
async code synchronously, and prevent duplication of logic.

This commit gets rid of all the local main loop contexts, and instead uses
sync functions.  To prevent duplication of logic, the async code now
leverages the sync code, in a thread.
---
 src/goabackend/goakerberosprovider.c | 963 +++++++++++++----------------------
 1 file changed, 345 insertions(+), 618 deletions(-)

diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c
index b125914..8bacd84 100644
--- a/src/goabackend/goakerberosprovider.c
+++ b/src/goabackend/goakerberosprovider.c
@@ -22,86 +22,121 @@
  *                    Stef Walter <stefw@gnome.org>
  */
 
 #include "config.h"
 #include <glib/gi18n-lib.h>
 
 #include "goalogging.h"
 #include "goaprovider.h"
 #include "goaprovider-priv.h"
 #include "goakerberosprovider.h"
 #include "goaeditablelabel.h"
 #include "goautils.h"
 #include "goaidentity.h"
 
 #include <gcr/gcr.h>
 
 #include "org.gnome.Identity.h"
 
 #include "org.freedesktop.realmd.h"
 
 /**
  * GoaKerberosProvider:
  *
  * The #GoaKerberosProvider structure contains only private request and should
  * only be accessed using the provided API.
  */
 struct _GoaKerberosProvider
 {
   /*< private >*/
   GoaProvider parent_instance;
-  GoaIdentityServiceManager *identity_manager;
-  GDBusObjectManager *object_manager;
 };
 
 typedef struct _GoaKerberosProviderClass GoaKerberosProviderClass;
 
 struct _GoaKerberosProviderClass
 {
   GoaProviderClass parent_class;
 };
 
+static GoaIdentityServiceManager *identity_manager;
+static GMutex identity_manager_mutex;
+static GCond identity_manager_condition;
+
+static GDBusObjectManager *object_manager;
+static GMutex object_manager_mutex;
+static GCond object_manager_condition;
+
+static void ensure_identity_manager (void);
+static void ensure_object_manager (void);
+
+static char *sign_in_identity_sync (GoaKerberosProvider  *self,
+                                    const char           *identifier,
+                                    const char           *password,
+                                    const char           *preauth_source,
+                                    GCancellable         *cancellable,
+                                    GError              **error);
+static void sign_in_thread (GSimpleAsyncResult  *result,
+                            GoaKerberosProvider *self,
+                            GCancellable        *cancellable);
+static GoaIdentityServiceIdentity *get_identity_from_object_manager (GoaKerberosProvider *self,
+                                                                     const char          *identifier);
+static gboolean dbus_proxy_reload_properties_sync (GDBusProxy    *proxy,
+                                                   GCancellable  *cancellable);
+
+static void goa_kerberos_provider_module_init (void);
+static void create_object_manager (void);
+static void create_identity_manager (void);
+
 /**
  * SECTION:goakerberosprovider
  * @title: GoaKerberosProvider
  * @short_description: A provider for enterprise identity servers
  *
  * #GoaKerberosProvider is used to access enterprise identity servers.
  */
 
 G_DEFINE_TYPE_WITH_CODE (GoaKerberosProvider, goa_kerberos_provider, GOA_TYPE_PROVIDER,
+                         goa_kerberos_provider_module_init ();
                          goa_provider_ensure_extension_points_registered ();
                          g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME,
                                                          g_define_type_id,
                                                          GOA_KERBEROS_NAME,
                                                          0));
 
+static void
+goa_kerberos_provider_module_init (void)
+{
+  create_object_manager ();
+  create_identity_manager ();
+}
+
 static const gchar *
 get_provider_type (GoaProvider *provider)
 {
   return GOA_KERBEROS_NAME;
 }
 
 static gchar *
 get_provider_name (GoaProvider *provider, GoaObject *object)
 {
   return g_strdup(_("Enterprise Login (Kerberos)"));
 }
 
 static GoaProviderGroup
 get_provider_group (GoaProvider *_provider)
 {
   return GOA_PROVIDER_GROUP_TICKETING;
 }
 
 typedef struct
 {
   GtkDialog *dialog;
   GMainLoop *loop;
 
   GtkWidget *cluebar;
   GtkWidget *cluebar_label;
   GtkWidget *connect_button;
   GtkWidget *progress_grid;
 
   GtkWidget *username;
   GtkWidget *realm_entry;
@@ -164,655 +199,195 @@ icon_released (GtkEntry             *entry,
 
   g_object_get (settings, "gtk-tooltip-timeout", &timeout, NULL);
   g_object_set (settings, "gtk-tooltip-timeout", 1, NULL);
   gtk_tooltip_trigger_tooltip_query (gtk_widget_get_display (GTK_WIDGET (entry)));
   g_object_set (settings, "gtk-tooltip-timeout", timeout, NULL);
 }
 
 static void
 set_entry_validation_error (GtkEntry    *entry,
                             GError      *error)
 {
   translate_error (&error);
   gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY,
                                      "dialog-error-symbolic");
   gtk_entry_set_icon_activatable (entry, GTK_ENTRY_ICON_SECONDARY, TRUE);
   g_signal_connect (entry, "icon-release", G_CALLBACK (icon_released), FALSE);
   g_signal_connect (entry, "query-tooltip", G_CALLBACK (query_tooltip), NULL);
   g_object_set (entry, "has-tooltip", TRUE, NULL);
   gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY,
                                    error->message);
 }
 
 static void
 clear_entry_validation_error (GtkEntry *entry)
 {
   g_object_set (entry, "has-tooltip", FALSE, NULL);
   gtk_entry_set_icon_from_pixbuf (entry, GTK_ENTRY_ICON_SECONDARY, NULL);
 }
 
 static void
-on_identity_signed_in (GoaIdentityServiceManager *manager,
-                       GAsyncResult              *result,
-                       GSimpleAsyncResult        *operation_result)
-{
-  gboolean  signed_in;
-  GError   *error;
-  char     *identity_object_path;
-
-  error = NULL;
-  signed_in = goa_identity_service_manager_call_sign_in_finish (manager,
-                                                                &identity_object_path,
-                                                                result,
-                                                                &error);
-
-  if (!signed_in)
-    {
-      translate_error (&error);
-
-      if (g_error_matches (error,
-                           G_IO_ERROR,
-                           G_IO_ERROR_CANCELLED))
-        {
-          g_clear_error (&error);
-          g_set_error_literal (&error,
-                               GOA_ERROR,
-                               GOA_ERROR_DIALOG_DISMISSED,
-                               "");
-        }
-
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  g_simple_async_result_set_op_res_gpointer (operation_result,
-                                             g_strdup (identity_object_path),
-                                             (GDestroyNotify)
-                                             g_free);
-  g_simple_async_result_complete_in_idle (operation_result);
-  g_object_unref (operation_result);
-}
-
-static void
-on_identity_manager_ensured (GoaKerberosProvider *self,
-                             GAsyncResult        *result,
-                             GSimpleAsyncResult  *operation_result)
-{
-  GoaIdentityServiceManager *manager;
-  GError             *error;
-
-  error = NULL;
-  manager = goa_identity_service_manager_proxy_new_for_bus_finish (result, &error);
-  if (manager == NULL)
-    {
-      translate_error (&error);
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  g_simple_async_result_set_op_res_gpointer (operation_result,
-                                             g_object_ref (manager),
-                                             (GDestroyNotify)
-                                             g_object_unref);
-  g_simple_async_result_complete_in_idle (operation_result);
-  g_object_unref (operation_result);
-}
-
-static void
-ensure_identity_manager (GoaKerberosProvider *self,
-                         GCancellable        *cancellable,
-                         GAsyncReadyCallback  callback,
-                         gpointer             user_data)
-{
-  GSimpleAsyncResult *operation_result;
-
-  operation_result = g_simple_async_result_new (G_OBJECT (self),
-                                                callback,
-                                                user_data,
-                                                ensure_identity_manager);
-  g_simple_async_result_set_check_cancellable (operation_result, cancellable);
-
-  g_object_set_data (G_OBJECT (operation_result),
-                     "cancellable",
-                     cancellable);
-
-  if (self->identity_manager != NULL)
-    {
-      g_simple_async_result_set_op_res_gpointer (operation_result,
-                                                 g_object_ref (self->identity_manager),
-                                                 (GDestroyNotify)
-                                                 g_object_unref);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  goa_identity_service_manager_proxy_new_for_bus (G_BUS_TYPE_SESSION,
-                                                  G_DBUS_PROXY_FLAGS_NONE,
-                                                  "org.gnome.Identity",
-                                                  "/org/gnome/Identity/Manager",
-                                                  cancellable,
-                                                  (GAsyncReadyCallback)
-                                                  on_identity_manager_ensured,
-                                                  operation_result);
-}
-
-static void
-on_object_manager_ensured (GoaKerberosProvider *self,
-                           GAsyncResult        *result,
-                           GSimpleAsyncResult  *operation_result)
-{
-  GDBusObjectManager *manager;
-  GError *error;
-
-  error = NULL;
-  manager = goa_identity_service_object_manager_client_new_for_bus_finish (result, &error);
-  if (manager == NULL)
-    {
-      translate_error (&error);
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  g_simple_async_result_set_op_res_gpointer (operation_result,
-                                             g_object_ref (manager),
-                                             (GDestroyNotify)
-                                             g_object_unref);
-  g_simple_async_result_complete_in_idle (operation_result);
-  g_object_unref (operation_result);
-}
-
-static void
-ensure_object_manager (GoaKerberosProvider *self,
-                       GCancellable        *cancellable,
-                       GAsyncReadyCallback  callback,
-                       gpointer             user_data)
-{
-  GSimpleAsyncResult *operation_result;
-
-  operation_result = g_simple_async_result_new (G_OBJECT (self),
-                                                callback,
-                                                user_data,
-                                                ensure_object_manager);
-  g_simple_async_result_set_check_cancellable (operation_result, cancellable);
-
-  g_object_set_data (G_OBJECT (operation_result),
-                     "cancellable",
-                     cancellable);
-
-  if (self->object_manager != NULL)
-    {
-      g_simple_async_result_set_op_res_gpointer (operation_result,
-                                                 g_object_ref (self->object_manager),
-                                                 (GDestroyNotify)
-                                                 g_object_unref);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-  goa_identity_service_object_manager_client_new_for_bus (G_BUS_TYPE_SESSION,
-                                                          G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
-                                                          "org.gnome.Identity",
-                                                          "/org/gnome/Identity",
-                                                          cancellable,
-                                                          (GAsyncReadyCallback)
-                                                          on_object_manager_ensured,
-                                                          operation_result);
-}
-
-static void
-on_secret_keys_exchanged_for_sign_in (GoaKerberosProvider *self,
-                                      GAsyncResult        *result,
-                                      GSimpleAsyncResult  *operation_result)
-{
-  const char       *identifier;
-  const char       *password;
-  const char       *preauth_source;
-  GCancellable     *cancellable;
-  GError           *error;
-  GVariantBuilder   details;
-
-  error = NULL;
-
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
-                                             &error))
-    {
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
-  password = g_object_get_data (G_OBJECT (operation_result), "password");
-  preauth_source = g_object_get_data (G_OBJECT (operation_result), "preauthentication-source");
-  identifier = g_simple_async_result_get_source_tag (operation_result);
-
-  g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
-
-  if (password != NULL)
-    {
-      GcrSecretExchange *secret_exchange;
-      char *secret;
-
-      secret_exchange = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-
-      secret = gcr_secret_exchange_send (secret_exchange, password, -1);
-      g_variant_builder_add (&details, "{ss}", "initial-password", secret);
-      g_free (secret);
-    }
-
-  if (preauth_source != NULL)
-    {
-      g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauth_source);
-    }
-
-  goa_identity_service_manager_call_sign_in (self->identity_manager,
-                                             identifier,
-                                             g_variant_builder_end (&details),
-                                             cancellable,
-                                             (GAsyncReadyCallback)
-                                             on_identity_signed_in,
-                                             operation_result);
-}
-
-static void
-on_secret_keys_exchanged (GoaIdentityServiceManager *manager,
-                          GAsyncResult              *result,
-                          GSimpleAsyncResult        *operation_result)
-{
-  GcrSecretExchange *secret_exchange;
-  char              *return_key;
-  GError            *error;
-
-  secret_exchange = g_simple_async_result_get_source_tag (operation_result);
-
-  error = NULL;
-  if (!goa_identity_service_manager_call_exchange_secret_keys_finish (manager,
-                                                                      &return_key,
-                                                                      result,
-                                                                      &error))
-    {
-      g_object_unref (secret_exchange);
-
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  if (!gcr_secret_exchange_receive (secret_exchange, return_key))
-    {
-      g_object_unref (secret_exchange);
-
-      g_simple_async_result_set_error (operation_result,
-                                       GCR_ERROR,
-                                       GCR_ERROR_UNRECOGNIZED,
-                                       _("Identity service returned invalid key"));
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  g_simple_async_result_set_op_res_gpointer (operation_result,
-                                             secret_exchange,
-                                             (GDestroyNotify)
-                                             g_object_unref);
-  g_simple_async_result_complete_in_idle (operation_result);
-  g_object_unref (operation_result);
-}
-
-static void
-exchange_secret_keys (GoaKerberosProvider  *self,
-                      const char           *password,
-                      GCancellable         *cancellable,
-                      GAsyncReadyCallback   callback,
-                      gpointer              user_data)
-{
-
-  GSimpleAsyncResult *operation_result;
-  GcrSecretExchange  *secret_exchange;
-  char               *secret_key;
-
-  secret_exchange = gcr_secret_exchange_new (NULL);
-
-  operation_result = g_simple_async_result_new (G_OBJECT (self),
-                                                callback,
-                                                user_data,
-                                                secret_exchange);
-
-  if (password == NULL)
-    {
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                                 NULL,
-                                                 NULL);
-      return;
-    }
-
-  secret_key = gcr_secret_exchange_begin (secret_exchange);
-
-  goa_identity_service_manager_call_exchange_secret_keys (self->identity_manager,
-                                                          secret_key,
-                                                          cancellable,
-                                                          (GAsyncReadyCallback)
-                                                          on_secret_keys_exchanged,
-                                                          operation_result);
-  g_free (secret_key);
-}
-
-static void
-on_identity_manager_ensured_for_sign_in (GoaKerberosProvider *self,
-                                         GAsyncResult        *result,
-                                         GSimpleAsyncResult  *operation_result)
-{
-  GoaIdentityServiceManager *manager;
-  const char                *password;
-  GCancellable              *cancellable;
-  GError                    *error;
-
-  error = NULL;
-
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
-                                             &error))
-    {
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  manager = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-
-  if (self->identity_manager == NULL)
-    self->identity_manager = g_object_ref (manager);
-
-  cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
-  password = g_object_get_data (G_OBJECT (operation_result), "password");
-
-  exchange_secret_keys (self,
-                        password,
-                        cancellable,
-                        (GAsyncReadyCallback)
-                        on_secret_keys_exchanged_for_sign_in,
-                        operation_result);
-}
-
-static void
 sign_in_identity (GoaKerberosProvider  *self,
                   const char           *identifier,
                   const char           *password,
                   const char           *preauth_source,
                   GCancellable         *cancellable,
                   GAsyncReadyCallback   callback,
                   gpointer              user_data)
 {
   GSimpleAsyncResult *operation_result;
 
   operation_result = g_simple_async_result_new (G_OBJECT (self),
                                                 callback,
                                                 user_data,
                                                 (gpointer)
                                                 identifier);
 
   g_simple_async_result_set_check_cancellable (operation_result, cancellable);
 
   g_object_set_data (G_OBJECT (operation_result),
                      "cancellable",
                      cancellable);
   g_object_set_data (G_OBJECT (operation_result),
                      "password",
                      (gpointer)
                      password);
-
   g_object_set_data_full (G_OBJECT (operation_result),
                           "preauthentication-source",
                           g_strdup (preauth_source),
                           g_free);
-
-  ensure_identity_manager (self,
-                           cancellable,
-                           (GAsyncReadyCallback)
-                           on_identity_manager_ensured_for_sign_in,
-                           operation_result);
-}
-
-static void
-on_object_manager_ensured_for_look_up (GoaKerberosProvider *self,
-                                       GAsyncResult        *result,
-                                       GSimpleAsyncResult  *operation_result)
-{
-  GDBusObjectManager *manager;
-  const char         *identifier;
-  GList              *objects, *node;
-  GError             *error;
-  gboolean            found;
-
-  error = NULL;
-  found = FALSE;
-
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result),
-                                             &error))
-    {
-
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  manager = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-
-  if (self->object_manager == NULL)
-    self->object_manager = g_object_ref (manager);
-
-  identifier = g_simple_async_result_get_source_tag (operation_result);
-
-  g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                             NULL,
-                                             NULL);
-  objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (self->object_manager));
-
-  for (node = objects; node != NULL; node = node->next)
-    {
-      GoaIdentityServiceIdentity *candidate_identity;
-      const char                 *candidate_identifier;
-      GDBusObject                *object;
-
-      object = node->data;
-
-      candidate_identity = GOA_IDENTITY_SERVICE_IDENTITY (g_dbus_object_get_interface (object, "org.gnome.Identity"));
-
-      if (candidate_identity == NULL)
-        continue;
-
-      candidate_identifier = goa_identity_service_identity_get_identifier (candidate_identity);
-
-      if (g_strcmp0 (candidate_identifier, identifier) == 0)
-        {
-          g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                                     candidate_identity,
-                                                     (GDestroyNotify)
-                                                     g_object_unref);
-          found = TRUE;
-          break;
-        }
-
-      g_object_unref (candidate_identity);
-    }
-
-  if (!found)
-    g_simple_async_result_set_error (operation_result, GOA_ERROR, GOA_ERROR_FAILED, "Failed to find an identity");
-
-  g_list_free_full (objects, (GDestroyNotify) g_object_unref);
-  g_simple_async_result_complete_in_idle (G_SIMPLE_ASYNC_RESULT (operation_result));
-  g_object_unref (operation_result);
-}
-
-static void
-look_up_identity (GoaKerberosProvider  *self,
-                  const char           *identifier,
-                  GCancellable         *cancellable,
-                  GAsyncReadyCallback   callback,
-                  gpointer              user_data)
-{
-  GSimpleAsyncResult *operation_result;
-
-  operation_result = g_simple_async_result_new (G_OBJECT (self),
-                                                callback,
-                                                user_data,
-                                                (gpointer)
-                                                identifier);
-
-  g_simple_async_result_set_check_cancellable (operation_result, cancellable);
-
-  g_object_set_data (G_OBJECT (operation_result),
-                     "cancellable",
-                     cancellable);
-  ensure_object_manager (self,
-                         cancellable,
-                         (GAsyncReadyCallback)
-                         on_object_manager_ensured_for_look_up,
-                         operation_result);
+  g_simple_async_result_run_in_thread (operation_result,
+                                       (GSimpleAsyncThreadFunc)
+                                       sign_in_thread,
+                                       G_PRIORITY_DEFAULT,
+                                       cancellable);
 }
 
 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;
+  GError              *sign_in_error;
   GoaAccount          *account;
   GoaTicketing        *ticketing;
   GVariant            *details;
   const char          *identifier;
   const char          *password;
   const char          *preauth_source;
-  SignInRequest        request;
+  char                *object_path = NULL;
   gboolean             ret;
 
   ret = FALSE;
 
   account = goa_object_peek_account (object);
   identifier = goa_account_get_identity (account);
 
   ticketing = goa_object_get_ticketing (GOA_OBJECT (object));
   details = goa_ticketing_get_details (ticketing);
 
   preauth_source = NULL;
   g_variant_lookup (details, "preauthentication-source", "&s", &preauth_source);
 
   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;
     }
   else if (credentials != NULL)
     {
       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,
-                    preauth_source,
-                    cancellable,
-                    (GAsyncReadyCallback)
-                    on_account_signed_in,
-                    &request);
-
-  g_main_loop_run (request.loop);
-  g_main_loop_unref (request.loop);
+  sign_in_error = NULL;
+  object_path = sign_in_identity_sync (self,
+                                       identifier,
+                                       password,
+                                       preauth_source,
+                                       cancellable,
+                                       &sign_in_error);
 
-  if (request.error != NULL)
+  if (sign_in_error != NULL)
     {
-      g_propagate_error (error, request.error);
+      g_propagate_error (error, sign_in_error);
       goto out;
     }
 
   ret = TRUE;
 out:
   g_clear_object (&ticketing);
+  g_free (object_path);
 
   if (credentials != NULL)
     g_variant_unref (credentials);
 
   return ret;
 }
 
 static void
 notify_is_temporary_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
 {
   GoaAccount *account;
   gboolean is_temporary;
 
   account = GOA_ACCOUNT (object);
   g_object_get (account, "is-temporary", &is_temporary, NULL);
 
   /* Toggle IsTemporary */
   goa_utils_keyfile_set_boolean (account, "IsTemporary", is_temporary);
 
   /* Set/unset SessionId */
   if (is_temporary)
     {
       GDBusConnection *connection;
       const gchar *guid;
 
       connection = G_DBUS_CONNECTION (user_data);
       guid = g_dbus_connection_get_guid (connection);
       goa_utils_keyfile_set_string (account, "SessionId", guid);
     }
   else
@@ -1754,235 +1329,387 @@ start_over:
     }
 
   g_free (request.account_object_path);
   g_free (principal);
   g_free (principal_for_display);
   if (request.loop != NULL)
     g_main_loop_unref (request.loop);
   return object;
 }
 
 static void
 show_account (GoaProvider *provider,
               GoaClient   *client,
               GoaObject   *object,
               GtkBox      *vbox,
               GtkGrid     *left,
               GtkGrid     *right)
 {
   GOA_PROVIDER_CLASS (goa_kerberos_provider_parent_class)->show_account (provider,
                                                                          client,
                                                                          object,
                                                                          vbox,
                                                                          left,
                                                                          right);
   goa_util_add_row_switch_from_keyfile_with_blurb (left, right, object,
                                                    _("Use for"),
                                                    "ticketing-disabled",
                                                    _("Network _Resources"));
 }
 
-static void
-on_identity_looked_up (GoaKerberosProvider *provider,
-                       GAsyncResult        *result,
-                       GSimpleAsyncResult  *operation_result)
-{
-
-  GoaIdentityServiceIdentity *identity;
-  GError                     *error;
-
-  error = NULL;
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
-    {
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  identity = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-  if (identity != NULL)
-    g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                               g_object_ref (identity),
-                                               (GDestroyNotify)
-                                               g_object_unref);
-  else
-    g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                               NULL,
-                                               NULL);
-
-  g_simple_async_result_complete_in_idle (operation_result);
-  g_object_unref (operation_result);
-}
-
-static void
-on_identity_looked_up_to_ensure_credentials (GoaKerberosProvider *self,
-                                             GAsyncResult        *result,
-                                             GSimpleAsyncResult  *operation_result)
+static gboolean
+dbus_proxy_reload_properties_sync (GDBusProxy    *proxy,
+                                   GCancellable  *cancellable)
 {
-
-  GoaIdentityServiceIdentity *identity;
-  GError                     *error;
-  GoaObject                  *object;
-  GoaAccount                 *account;
-  const char                 *identifier;
-  GCancellable               *cancellable;
-
-  error = NULL;
-  if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error))
-    {
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  identity = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
-
-  if (identity != NULL && goa_identity_service_identity_get_is_signed_in (identity))
+  GVariant *result;
+  char *name;
+  GVariant *value;
+  GVariantIter *iter;
+
+  result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy),
+                                        g_dbus_proxy_get_name_owner (proxy),
+                                        g_dbus_proxy_get_object_path (proxy),
+                                        "org.freedesktop.DBus.Properties",
+                                        "GetAll",
+                                        g_variant_new ("(s)", g_dbus_proxy_get_interface_name (proxy)),
+                                        G_VARIANT_TYPE ("(a{sv})"),
+                                        G_DBUS_CALL_FLAGS_NONE,
+                                        -1,
+                                        cancellable,
+                                        NULL);
+  if (result == NULL)
+    return FALSE;
+
+  g_variant_get (result, "(a{sv})", &iter);
+  while (g_variant_iter_next (iter, "{sv}", &name, &value))
     {
-      g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (operation_result),
-                                                 g_object_ref (identity),
-                                                 (GDestroyNotify)
-                                                 g_object_unref);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
-    }
-
-  object = GOA_OBJECT (g_async_result_get_source_object (G_ASYNC_RESULT (operation_result)));
-  cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable");
+      g_dbus_proxy_set_cached_property (proxy, name, value);
 
-  if (!get_ticket_sync (self,
-                        object,
-                        FALSE /* Don't allow interaction */,
-                        cancellable,
-                        &error))
-    {
-      g_simple_async_result_take_error (operation_result, error);
-      g_simple_async_result_complete_in_idle (operation_result);
-      g_object_unref (operation_result);
-      return;
+      g_free (name);
+      g_variant_unref (value);
     }
-
-  account = goa_object_peek_account (object);
-  identifier = goa_account_get_identity (account);
-
-  look_up_identity (self,
-                    identifier,
-                    cancellable,
-                    (GAsyncReadyCallback)
-                    on_identity_looked_up,
-                    operation_result);
-}
-
-static void
-on_credentials_ensured (GoaObject    *object,
-                        GAsyncResult *result,
-                        GMainLoop    *loop)
-{
-  g_main_loop_quit (loop);
+  g_variant_iter_free (iter);
+  return TRUE;
 }
 
 static gboolean
 ensure_credentials_sync (GoaProvider    *provider,
                          GoaObject      *object,
                          gint           *out_expires_in,
                          GCancellable   *cancellable,
                          GError        **error)
 {
-  GoaIdentityServiceIdentity *identity;
+  GoaIdentityServiceIdentity *identity = NULL;
   GoaAccount                 *account;
   const char                 *identifier;
-  GSimpleAsyncResult         *operation_result;
-  GMainLoop                  *loop;
-  GMainContext               *context;
   gint64                      timestamp;
   GDateTime                  *now, *expiration_time;
   GTimeSpan                   time_span;
-  GError                     *lookup_error;
+  gboolean                    credentials_ensured = FALSE;
 
   account = goa_object_peek_account (object);
   identifier = goa_account_get_identity (account);
 
-  context = g_main_context_new ();
-  g_main_context_push_thread_default (context);
-  loop = g_main_loop_new (context, FALSE);
-  operation_result = g_simple_async_result_new (G_OBJECT (object),
-                                                (GAsyncReadyCallback)
-                                                on_credentials_ensured,
-                                                loop,
-                                                ensure_credentials_sync);
-  g_simple_async_result_set_check_cancellable (operation_result, cancellable);
-
-  g_object_set_data (G_OBJECT (operation_result),
-                     "cancellable",
-                     cancellable);
+  ensure_identity_manager ();
 
-  g_object_ref (operation_result);
-  look_up_identity (GOA_KERBEROS_PROVIDER (provider),
-                    identifier,
-                    cancellable,
-                    (GAsyncReadyCallback)
-                    on_identity_looked_up_to_ensure_credentials,
-                    operation_result);
+  g_mutex_lock (&identity_manager_mutex);
+  identity = get_identity_from_object_manager (GOA_KERBEROS_PROVIDER (provider),
+                                               identifier);
 
-  g_main_loop_run (loop);
-  g_main_loop_unref (loop);
-
-  g_main_context_pop_thread_default (context);
-  g_main_context_unref (context);
+  if (identity != NULL)
+    {
+      if (!dbus_proxy_reload_properties_sync (G_DBUS_PROXY (identity), cancellable))
+        g_clear_object (&identity);
+    }
 
-  lookup_error = NULL;
-  if (g_simple_async_result_propagate_error (operation_result, &lookup_error))
+  if (identity == NULL || !goa_identity_service_identity_get_is_signed_in (identity))
     {
-      translate_error (&lookup_error);
-      g_set_error_literal (error,
-                           GOA_ERROR,
-                           GOA_ERROR_NOT_AUTHORIZED,
-                           lookup_error->message);
-      g_error_free (lookup_error);
-      g_object_unref (operation_result);
-      return FALSE;
+      gboolean ticket_synced;
+
+      g_mutex_unlock (&identity_manager_mutex);
+      ticket_synced = get_ticket_sync (GOA_KERBEROS_PROVIDER (provider),
+                                       object,
+                                       FALSE /* Don't allow interaction */,
+                                       cancellable,
+                                       error);
+      g_mutex_lock (&identity_manager_mutex);
+
+      if (!ticket_synced)
+        goto out;
+
+      if (identity == NULL)
+        identity = get_identity_from_object_manager (GOA_KERBEROS_PROVIDER (provider),
+                                                     identifier);
     }
 
-  identity = g_simple_async_result_get_op_res_gpointer (operation_result);
+  if (identity == NULL)
+    goto out;
+
+  dbus_proxy_reload_properties_sync (G_DBUS_PROXY (identity), cancellable);
 
   now = g_date_time_new_now_local ();
   timestamp = goa_identity_service_identity_get_expiration_timestamp (identity);
+
   expiration_time = g_date_time_new_from_unix_local (timestamp);
   time_span = g_date_time_difference (expiration_time, now);
 
   time_span /= G_TIME_SPAN_SECOND;
 
   if (time_span < 0 || time_span > G_MAXINT)
     time_span = 0;
 
   *out_expires_in = (int) time_span;
+  credentials_ensured = TRUE;
 
   g_date_time_unref (now);
   g_date_time_unref (expiration_time);
-  g_object_unref (operation_result);
 
-  return TRUE;
+out:
+  g_clear_object (&identity);
+  g_mutex_unlock (&identity_manager_mutex);
+  return credentials_ensured;
+}
+
+static GoaIdentityServiceIdentity *
+get_identity_from_object_manager (GoaKerberosProvider *self,
+                                  const char          *identifier)
+{
+  GoaIdentityServiceIdentity *identity = NULL;
+  GList                      *objects, *node;
+
+  ensure_object_manager ();
+
+  g_mutex_lock (&object_manager_mutex);
+  objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (object_manager));
+
+  for (node = objects; node != NULL; node = node->next)
+    {
+      GoaIdentityServiceIdentity *candidate_identity;
+      const char                 *candidate_identifier;
+      GDBusObject                *object;
+
+      object = node->data;
+
+      candidate_identity = GOA_IDENTITY_SERVICE_IDENTITY (g_dbus_object_get_interface (object, "org.gnome.Identity"));
+
+      if (candidate_identity == NULL)
+        continue;
+
+      candidate_identifier = goa_identity_service_identity_get_identifier (candidate_identity);
+
+      if (g_strcmp0 (candidate_identifier, identifier) == 0)
+        {
+          identity = candidate_identity;
+          break;
+        }
+
+      g_object_unref (candidate_identity);
+    }
+
+  g_list_free_full (objects, (GDestroyNotify) g_object_unref);
+  g_mutex_unlock (&object_manager_mutex);
+
+  return identity;
+}
+
+static char *
+sign_in_identity_sync (GoaKerberosProvider  *self,
+                       const char           *identifier,
+                       const char           *password,
+                       const char           *preauth_source,
+                       GCancellable         *cancellable,
+                       GError              **error)
+{
+  GcrSecretExchange  *secret_exchange;
+  char               *secret_key;
+  char               *return_key;
+  char               *concealed_secret;
+  char               *identity_object_path = NULL;
+  gboolean            keys_exchanged;
+  GVariantBuilder     details;
+
+  secret_exchange = gcr_secret_exchange_new (NULL);
+
+  secret_key = gcr_secret_exchange_begin (secret_exchange);
+  ensure_identity_manager ();
+
+  g_mutex_lock (&identity_manager_mutex);
+  keys_exchanged = goa_identity_service_manager_call_exchange_secret_keys_sync (identity_manager,
+                                                                                secret_key,
+                                                                                &return_key,
+                                                                                cancellable,
+                                                                                error);
+  g_mutex_unlock (&identity_manager_mutex);
+  g_free (secret_key);
+
+  if (!keys_exchanged)
+    goto out;
+
+  if (!gcr_secret_exchange_receive (secret_exchange, return_key))
+    {
+      g_set_error (error,
+                   GCR_ERROR,
+                   GCR_ERROR_UNRECOGNIZED,
+                   _("Identity service returned invalid key"));
+      goto out;
+    }
+
+  g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}"));
+
+  concealed_secret = gcr_secret_exchange_send (secret_exchange, password, -1);
+  g_variant_builder_add (&details, "{ss}", "initial-password", concealed_secret);
+  g_free (concealed_secret);
+
+  if (preauth_source != NULL)
+    {
+      g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauth_source);
+    }
+
+  g_mutex_lock (&identity_manager_mutex);
+  goa_identity_service_manager_call_sign_in_sync (identity_manager,
+                                                  identifier,
+                                                  g_variant_builder_end (&details),
+                                                  &identity_object_path,
+                                                  cancellable,
+                                                  error);
+  g_mutex_unlock (&identity_manager_mutex);
+
+out:
+  g_object_unref (secret_exchange);
+  return identity_object_path;
+}
+
+static void
+sign_in_thread (GSimpleAsyncResult  *result,
+                GoaKerberosProvider *self,
+                GCancellable        *cancellable)
+{
+  const char *identifier;
+  const char *password;
+  const char *preauth_source;
+  char *object_path;
+  GError *error;
+
+  identifier = g_simple_async_result_get_source_tag (result);
+  password = g_object_get_data (G_OBJECT (result), "password");
+  preauth_source = g_object_get_data (G_OBJECT (result), "preauth-source");
+
+  error = NULL;
+  object_path = sign_in_identity_sync (self, identifier, password, preauth_source, cancellable, &error);
+
+  if (object_path == NULL)
+    g_simple_async_result_take_error (result, error);
+  else
+    g_simple_async_result_set_op_res_gpointer (result, object_path, NULL);
+}
+
+
+static void
+on_object_manager_created (gpointer             object,
+                           GAsyncResult        *result,
+                           GSimpleAsyncResult  *operation_result)
+{
+  GDBusObjectManager *manager;
+  GError *error;
+
+  error = NULL;
+  manager = goa_identity_service_object_manager_client_new_for_bus_finish (result, &error);
+  if (manager == NULL)
+    {
+      g_warning ("GoaKerberosProvider: Could not connect to identity service: %s", error->message);
+      g_clear_error (&error);
+      return;
+    }
+
+  g_mutex_lock (&object_manager_mutex);
+  object_manager = manager;
+  g_cond_signal (&object_manager_condition);
+  g_mutex_unlock (&object_manager_mutex);
+}
+
+static void
+create_object_manager (void)
+{
+  goa_identity_service_object_manager_client_new_for_bus (G_BUS_TYPE_SESSION,
+                                                          G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+                                                          "org.gnome.Identity",
+                                                          "/org/gnome/Identity",
+                                                          NULL,
+                                                          (GAsyncReadyCallback)
+                                                          on_object_manager_created,
+                                                          NULL);
+}
+
+static void
+ensure_object_manager (void)
+{
+  g_mutex_lock (&object_manager_mutex);
+  while (object_manager == NULL)
+      g_cond_wait (&object_manager_condition, &object_manager_mutex);
+  g_mutex_unlock (&object_manager_mutex);
+}
+
+static void
+on_identity_manager_created (gpointer             identity,
+                             GAsyncResult        *result,
+                             GSimpleAsyncResult  *operation_result)
+{
+  GoaIdentityServiceManager *manager;
+  GError *error;
+
+  error = NULL;
+  manager = goa_identity_service_manager_proxy_new_for_bus_finish (result, &error);
+  if (manager == NULL)
+    {
+      g_warning ("GoaKerberosProvider: Could not connect to identity service manager: %s", error->message);
+      g_clear_error (&error);
+      return;
+    }
+
+  g_mutex_lock (&identity_manager_mutex);
+  identity_manager = manager;
+  g_cond_signal (&identity_manager_condition);
+  g_mutex_unlock (&identity_manager_mutex);
+}
+
+static void
+create_identity_manager (void)
+{
+  goa_identity_service_manager_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                                                  G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+                                                  "org.gnome.Identity",
+                                                  "/org/gnome/Identity/Manager",
+                                                  NULL,
+                                                  (GAsyncReadyCallback)
+                                                  on_identity_manager_created,
+                                                  NULL);
+}
+
+static void
+ensure_identity_manager (void)
+{
+  g_mutex_lock (&identity_manager_mutex);
+  while (identity_manager == NULL)
+      g_cond_wait (&identity_manager_condition, &identity_manager_mutex);
+  g_mutex_unlock (&identity_manager_mutex);
 }
 
 static void
 goa_kerberos_provider_init (GoaKerberosProvider *provider)
 {
 }
 
 static void
 goa_kerberos_provider_class_init (GoaKerberosProviderClass *kerberos_class)
 {
   GoaProviderClass *provider_class;
 
   provider_class = GOA_PROVIDER_CLASS (kerberos_class);
   provider_class->get_provider_type          = get_provider_type;
   provider_class->get_provider_name          = get_provider_name;
   provider_class->get_provider_group         = get_provider_group;
   provider_class->build_object               = build_object;
   provider_class->add_account                = add_account;
   provider_class->refresh_account            = refresh_account;
   provider_class->show_account               = show_account;
   provider_class->ensure_credentials_sync    = ensure_credentials_sync;
 }
-- 
1.8.3.1


From 18f286fa141b4615b3e8a9d98e02093170afb251 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Tue, 28 Oct 2014 17:10:49 -0400
Subject: [PATCH 10/10] identity: separate identity service off into its own
 process

This commit segregates the kerberos specific functionality off
into its own helper process.

This has a couple of benefits:

1) It is actually a better fit for how the code was initially designed,
which was first staged in gnome-settings-daemon with g-o-a talking to
it. Right now we have gnome-online-accounts talking to itself,
in-process, through d-bus, which is suboptimal.

2) It keeps any leaks or crashes in the kerberos code from bringing down
the whole online accounts daemon.
---
 data/Makefile.am                     |  8 ++++-
 data/org.gnome.Identity.service.in   |  3 ++
 src/daemon/Makefile.am               |  8 -----
 src/daemon/goadaemon.c               | 43 +++++++++++++-------------
 src/goaidentity/Makefile.am          | 14 ++++++---
 src/goaidentity/goaidentityservice.c |  2 +-
 src/goaidentity/main.c               | 59 ++++++++++++++++++++++++++++++++++++
 7 files changed, 100 insertions(+), 37 deletions(-)
 create mode 100644 data/org.gnome.Identity.service.in
 create mode 100644 src/goaidentity/main.c

diff --git a/data/Makefile.am b/data/Makefile.am
index cb30eb8..fb68063 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,37 +1,43 @@
 
 NULL =
 
 SUBDIRS = icons
 
 cssdir = $(pkgdatadir)
 css_DATA = goawebview.css
 
 gsettings_in_files = org.gnome.online-accounts.gschema.xml.in
 gsettings_SCHEMAS = $(gsettings_in_files:.xml.in=.xml)
 
 @INTLTOOL_XML_RULE@
 @GSETTINGS_RULES@
 
 servicedir       = $(datadir)/dbus-1/services
 service_in_files = org.gnome.OnlineAccounts.service.in
+
+if BUILD_KERBEROS
+service_in_files += org.gnome.Identity.service.in
+endif
+
 service_DATA     = $(service_in_files:.service.in=.service)
 
-$(service_DATA): $(service_in_files) Makefile
+%.service: %.service.in Makefile
 	@sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
 
 EXTRA_DIST =						\
 	$(css_DATA)					\
 	$(gsettings_in_files)				\
 	$(service_in_files)				\
 	dbus-interfaces.xml				\
 	$(NULL)
 
 DISTCLEANFILES =					\
 	$(gsettings_SCHEMAS)				\
 	org.gnome.OnlineAccounts.service		\
+	org.gnome.Identity.service			\
 	$(NULL)
 
 clean-local :
 	rm -f *~
 
 -include $(top_srcdir)/git.mk
diff --git a/data/org.gnome.Identity.service.in b/data/org.gnome.Identity.service.in
new file mode 100644
index 0000000..bd3b032
--- /dev/null
+++ b/data/org.gnome.Identity.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.gnome.Identity
+Exec=@libexecdir@/goa-identity-service
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 72dde8a..a1047b1 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -21,42 +21,34 @@ INCLUDES = 							\
 BUILT_SOURCES = $(NULL)
 EXTRA_DIST = $(NULL)
 
 libexec_PROGRAMS = goa-daemon
 
 goa_daemon_SOURCES = 						\
 				main.c				\
 	goadaemontypes.h					\
 	goadaemon.h		goadaemon.c			\
 	$(NULL)
 
 goa_daemon_CPPFLAGS =						\
 	-DG_LOG_DOMAIN=\"goa-daemon\"				\
 	-DGOA_BACKEND_COMPILATION				\
 	$(NULL)
 
 goa_daemon_CFLAGS =						\
 	$(GLIB_CFLAGS) 						\
 	$(GTK_CFLAGS)						\
 	$(REST_CFLAGS)						\
 	$(NULL)
 
 goa_daemon_LDADD = 						\
 	$(GLIB_LIBS) 						\
 	$(top_builddir)/src/goa/libgoa-1.0.la			\
 	$(top_builddir)/src/goabackend/libgoa-backend-1.0.la	\
 	$(GTK_LIBS)						\
 	$(REST_LIBS)						\
 	$(NULL)
 
-if BUILD_KERBEROS
-goa_daemon_LDADD += 						\
-	$(top_builddir)/src/goaidentity/libgoaidentity.la	\
-	$(KRB5_LIBS)						\
-	$(GCR_LIBS)						\
-	$(NULL)
-endif
-
 clean-local :
 	rm -f *~
 
 -include $(top_srcdir)/git.mk
diff --git a/src/daemon/goadaemon.c b/src/daemon/goadaemon.c
index 1a6ac65..142170e 100644
--- a/src/daemon/goadaemon.c
+++ b/src/daemon/goadaemon.c
@@ -3,82 +3,75 @@
  * Copyright (C) 2011, 2012 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
  * Public License along with this library; if not, write to the
  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  * Boston, MA 02111-1307, USA.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
 
 #include "config.h"
 #include <glib/gi18n.h>
 #include <gio/gdesktopappinfo.h>
 #include <rest/rest-proxy.h>
 #include <libsoup/soup.h>
 
 #include "goadaemon.h"
 #include "goabackend/goabackend.h"
 #include "goabackend/goalogging.h"
 #include "goabackend/goautils.h"
-#ifdef GOA_KERBEROS_ENABLED
-#include "goaidentity/goaidentityservice.h"
-#endif
 
 struct _GoaDaemon
 {
   GObject parent_instance;
 
   GDBusConnection *connection;
 
   GFileMonitor *system_conf_dir_monitor;
   GFileMonitor *home_conf_file_monitor;
   GFileMonitor *home_conf_dir_monitor;
 
   GDBusObjectManagerServer *object_manager;
 
   GoaManager *manager;
 
-#ifdef GOA_KERBEROS_ENABLED
-  GoaIdentityService *identity_service;
-#endif
-
   guint config_timeout_id;
 };
 
 typedef struct
 {
   GObjectClass parent_class;
 } GoaDaemonClass;
 
 static void on_file_monitor_changed (GFileMonitor     *monitor,
                                      GFile            *file,
                                      GFile            *other_file,
                                      GFileMonitorEvent event_type,
                                      gpointer          user_data);
 
 static gboolean on_manager_handle_add_account (GoaManager            *object,
                                                GDBusMethodInvocation *invocation,
                                                const gchar           *provider_type,
                                                const gchar           *identity,
                                                const gchar           *presentation_identity,
                                                GVariant              *credentials,
                                                GVariant              *details,
                                                gpointer               user_data);
 
 static gboolean on_account_handle_remove (GoaAccount            *account,
                                           GDBusMethodInvocation *invocation,
                                           gpointer               user_data);
 
 static gboolean on_account_handle_ensure_credentials (GoaAccount            *account,
                                                       GDBusMethodInvocation *invocation,
                                                       gpointer               user_data);
@@ -90,184 +83,190 @@ G_DEFINE_TYPE (GoaDaemon, goa_daemon, G_TYPE_OBJECT);
 static void
 goa_daemon_finalize (GObject *object)
 {
   GoaDaemon *daemon = GOA_DAEMON (object);
 
   if (daemon->config_timeout_id != 0)
     {
       g_source_remove (daemon->config_timeout_id);
     }
 
   if (daemon->system_conf_dir_monitor != NULL)
     {
       g_signal_handlers_disconnect_by_func (daemon->system_conf_dir_monitor, on_file_monitor_changed, daemon);
       g_object_unref (daemon->system_conf_dir_monitor);
     }
   if (daemon->home_conf_dir_monitor != NULL)
     {
       g_signal_handlers_disconnect_by_func (daemon->home_conf_dir_monitor, on_file_monitor_changed, daemon);
       g_object_unref (daemon->home_conf_dir_monitor);
     }
   if (daemon->home_conf_file_monitor != NULL)
     {
       g_signal_handlers_disconnect_by_func (daemon->home_conf_file_monitor, on_file_monitor_changed, daemon);
       g_object_unref (daemon->home_conf_file_monitor);
     }
 
   g_object_unref (daemon->manager);
   g_object_unref (daemon->object_manager);
   g_object_unref (daemon->connection);
 
-#ifdef GOA_KERBEROS_ENABLED
-  g_clear_object (&daemon->identity_service);
-#endif
-
   G_OBJECT_CLASS (goa_daemon_parent_class)->finalize (object);
 }
 
 static GFileMonitor *
 create_monitor (const gchar *path, gboolean is_dir)
 {
   GFile *file;
   GFileMonitor *monitor;
   GError *error;
 
   error = NULL;
   file = g_file_new_for_path (path);
   if (is_dir)
     monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &error);
   else
     monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
 
   if (monitor == NULL)
     {
       goa_warning ("Error monitoring %s at %s: %s (%s, %d)",
                    is_dir ? "directory" : "file",
                    path,
                    error->message, g_quark_to_string (error->domain), error->code);
       g_error_free (error);
     }
   g_object_unref (file);
 
   return monitor;
 }
 
 static gboolean
 on_config_file_monitor_timeout (gpointer user_data)
 {
   GoaDaemon *daemon = GOA_DAEMON (user_data);
 
   daemon->config_timeout_id = 0;
   goa_info ("Reloading configuration files\n");
   goa_daemon_reload_configuration (daemon);
 
   return FALSE;
 }
 
 static void
 on_file_monitor_changed (GFileMonitor      *monitor,
                          GFile             *file,
                          GFile             *other_file,
                          GFileMonitorEvent  event_type,
                          gpointer           user_data)
 {
   GoaDaemon *daemon = GOA_DAEMON (user_data);
 
   if (daemon->config_timeout_id == 0)
     {
       daemon->config_timeout_id = g_timeout_add (200, on_config_file_monitor_timeout, daemon);
     }
 }
 
+#ifdef GOA_KERBEROS_ENABLED
+static void
+activate_identity_service (GoaDaemon *daemon)
+{
+  GoaProvider *provider;
+
+  /* We activate the identity service implicitly by using the kerberos
+   * backend.  This way if the kerberos backend isn't enabled, we don't
+   * end up starting the identity service needlessly
+   */
+  provider = goa_provider_get_for_provider_type (GOA_KERBEROS_NAME);
+
+  if (provider != NULL)
+    {
+      g_debug ("activated kerberos provider");
+      g_object_unref (provider);
+    }
+}
+#endif
+
 static void
 goa_daemon_init (GoaDaemon *daemon)
 {
   static volatile GQuark goa_error_domain = 0;
   GoaObjectSkeleton *object;
   gchar *path;
-#ifdef GOA_KERBEROS_ENABLED
-  GError *error = NULL;
-#endif
 
   /* this will force associating errors in the GOA_ERROR error domain
    * with org.freedesktop.Goa.Error.* errors via g_dbus_error_register_error_domain().
    */
   goa_error_domain = GOA_ERROR;
   goa_error_domain; /* shut up -Wunused-but-set-variable */
 
   /* TODO: maybe nicer to pass in a GDBusConnection* construct property */
   daemon->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
 
   /* Create object manager */
   daemon->object_manager = g_dbus_object_manager_server_new ("/org/gnome/OnlineAccounts");
 
   /* Create and export Manager */
   daemon->manager = goa_manager_skeleton_new ();
   g_signal_connect (daemon->manager,
                     "handle-add-account",
                     G_CALLBACK (on_manager_handle_add_account),
                     daemon);
   object = goa_object_skeleton_new ("/org/gnome/OnlineAccounts/Manager");
   goa_object_skeleton_set_manager (object, daemon->manager);
   g_dbus_object_manager_server_export (daemon->object_manager, G_DBUS_OBJECT_SKELETON (object));
   g_object_unref (object);
 
   /* create ~/.config/goa-1.0 directory */
   path = g_strdup_printf ("%s/goa-1.0", g_get_user_config_dir ());
   if (g_mkdir_with_parents (path, 0755) != 0)
     {
       goa_warning ("Error creating directory %s: %m", path);
     }
   g_free (path);
 
   /* set up file monitoring */
   path = g_strdup_printf ("%s/goa-1.0/accounts.conf", g_get_user_config_dir ());
   daemon->home_conf_file_monitor = create_monitor (path, FALSE);
   if (daemon->home_conf_file_monitor != NULL)
     g_signal_connect (daemon->home_conf_file_monitor, "changed", G_CALLBACK (on_file_monitor_changed), daemon);
   g_free (path);
 
   /* prime the list of accounts */
   goa_daemon_reload_configuration (daemon);
 
   /* Export objects */
   g_dbus_object_manager_server_set_connection (daemon->object_manager, daemon->connection);
 
 #ifdef GOA_KERBEROS_ENABLED
-  daemon->identity_service = goa_identity_service_new ();
-  if (!goa_identity_service_activate (daemon->identity_service,
-                                      &error))
-    {
-      goa_warning ("Error activating identity service: %s", error->message);
-      g_error_free (error);
-      g_clear_object (&daemon->identity_service);
-    }
+  activate_identity_service (daemon);
 #endif
 }
 
 static void
 goa_daemon_class_init (GoaDaemonClass *klass)
 {
   GObjectClass *gobject_class;
 
   gobject_class = G_OBJECT_CLASS (klass);
   gobject_class->finalize     = goa_daemon_finalize;
 }
 
 GoaDaemon *
 goa_daemon_new (void)
 {
   return GOA_DAEMON (g_object_new (GOA_TYPE_DAEMON, NULL));
 }
 
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static void
 diff_sorted_lists (GList *list1,
                    GList *list2,
                    GCompareFunc compare,
                    GList **added,
                    GList **removed,
                    GList **unchanged)
 {
   gint order;
diff --git a/src/goaidentity/Makefile.am b/src/goaidentity/Makefile.am
index dbce960..f7386e4 100644
--- a/src/goaidentity/Makefile.am
+++ b/src/goaidentity/Makefile.am
@@ -18,116 +18,120 @@ INCLUDES = 							\
 	-DGOA_API_IS_SUBJECT_TO_CHANGE				\
 	-DGOA_BACKEND_API_IS_SUBJECT_TO_CHANGE			\
 	$(WARN_CFLAGS)						\
 	$(NULL)
 
 identity_headers =						\
 	goaalarm.h						\
 	goaidentity.h						\
 	goaidentityinquiry.h					\
 	goaidentityinquiryprivate.h				\
 	goaidentityservice.h					\
 	goaidentitymanagerprivate.h				\
 	goaidentitymanager.h					\
 	goaidentityutils.h					\
 	goakerberosidentity.h					\
 	goakerberosidentityinquiry.h				\
 	goakerberosidentitymanager.h				\
 	$(NULL)
 
 identity_sources =						\
 	$(identity_headers)					\
 	goaalarm.c						\
 	goaidentity.c						\
 	goaidentityinquiry.c					\
 	goaidentityservice.c					\
 	goaidentitymanager.c					\
 	goaidentityutils.c					\
 	goakerberosidentity.c					\
 	goakerberosidentityinquiry.c				\
 	goakerberosidentitymanager.c				\
+	main.c							\
 	$(NULL)
 
 identity_dbus_built_sources =					\
 	org.gnome.Identity.c		org.gnome.Identity.h	\
 	$(NULL)
 
 $(identity_dbus_built_sources) : Makefile.am org.gnome.Identity.xml
 	gdbus-codegen						\
 		--interface-prefix org.gnome.Identity.		\
 		--c-namespace GoaIdentityService		\
 		--c-generate-object-manager			\
 		--generate-c-code org.gnome.Identity		\
 		$(srcdir)/org.gnome.Identity.xml		\
 		--annotate "org.gnome.Identity"			\
 		           "org.gtk.GDBus.C.Name" Identity	\
 		$(NULL)
 BUILT_SOURCES += $(identity_dbus_built_sources)
 EXTRA_DIST += org.gnome.Identity.xml
 
 goaidentityenumtypes.h: goaidentityenumtypes.h.in $(identity_headers)
 	$(AM_V_GEN) glib-mkenums --template $^ > $@
 EXTRA_DIST += goaidentityenumtypes.h.in
 BUILT_SOURCES += goaidentityenumtypes.h
 
 goaidentityenumtypes.c: goaidentityenumtypes.c.in $(identity_headers)
 	$(AM_V_GEN) glib-mkenums --template $^ > $@
 EXTRA_DIST += goaidentityenumtypes.c.in
 BUILT_SOURCES += goaidentityenumtypes.c
 
 realmd_dbus_built_sources =					\
 	org.freedesktop.realmd.h				\
 	org.freedesktop.realmd.c				\
 	$(NULL)
 
 $(realmd_dbus_built_sources) : Makefile.am org.freedesktop.realmd.xml
 	gdbus-codegen						\
 		--interface-prefix org.freedesktop.realmd.	\
 		--generate-c-code org.freedesktop.realmd	\
 		--c-generate-object-manager			\
 		--c-namespace GoaRealm				\
 		--annotate "org.freedesktop.realmd.Realm"	\
 		            org.gtk.GDBus.C.Name Common		\
 		$(srcdir)/org.freedesktop.realmd.xml		\
 		$(NULL)
 BUILT_SOURCES += $(realmd_dbus_built_sources)
 EXTRA_DIST += org.freedesktop.realmd.xml
 
 if BUILD_KERBEROS
-noinst_LTLIBRARIES = libgoaidentity.la
+libexec_PROGRAMS = goa-identity-service
 
-libgoaidentity_la_SOURCES = 					\
+goa_identity_service_SOURCES = 					\
 	goaidentityenumtypes.h		goaidentityenumtypes.c	\
+        $(top_srcdir)/src/goabackend/goalogging.h               \
+        $(top_srcdir)/src/goabackend/goalogging.c               \
 	$(identity_dbus_built_sources)				\
 	$(realmd_dbus_built_sources)				\
 	$(identity_sources)					\
 	$(NULL)
 
-libgoaidentity_la_CPPFLAGS =					\
+goa_identity_service_CPPFLAGS =					\
 	-DG_LOG_DOMAIN=\"libgoaidentity\"			\
 	-DGOA_BACKEND_COMPILATION				\
 	$(NULL)
 
-libgoaidentity_la_CFLAGS =					\
+goa_identity_service_CFLAGS =					\
 	$(GLIB_CFLAGS) 						\
 	$(GTK_CFLAGS)						\
 	$(KRB5_CFLAGS)						\
 	$(GCR_CFLAGS)						\
 	$(NULL)
 
-libgoaidentity_la_LIBADD = 					\
+goa_identity_service_LDADD = 					\
+	$(top_builddir)/src/goa/libgoa-1.0.la			\
 	$(GLIB_LIBS) 						\
 	$(GTK_LIBS)						\
 	$(KRB5_LIBS)						\
 	$(GCR_LIBS)						\
 	$(NULL)
 
 else
 EXTRA_DIST += $(identity_sources)
 endif
 
 CLEANFILES += $(BUILT_SOURCES)
 
 clean-local :
 	rm -f *~
 
 -include $(top_srcdir)/git.mk
diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c
index 883f549..52a3674 100644
--- a/src/goaidentity/goaidentityservice.c
+++ b/src/goaidentity/goaidentityservice.c
@@ -1735,61 +1735,61 @@ on_session_bus_acquired (GDBusConnection    *connection,
 
   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,
                     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");
+    raise (SIGTERM);
 }
 
 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->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,
diff --git a/src/goaidentity/main.c b/src/goaidentity/main.c
new file mode 100644
index 0000000..2de35ac
--- /dev/null
+++ b/src/goaidentity/main.c
@@ -0,0 +1,59 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/*
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <glib-unix.h>
+
+#include <gio/gio.h>
+
+#include "goaidentityservice.h"
+
+int
+main (int    argc,
+      char **argv)
+{
+  GMainLoop *loop;
+  GoaIdentityService *service;
+  GError *error;
+  int ret = 1;
+
+  loop = g_main_loop_new (NULL, FALSE);
+  service = goa_identity_service_new ();
+
+  error = NULL;
+  goa_identity_service_activate (service, &error);
+
+  if (error != NULL) {
+      g_warning ("couldn't activate identity service: %s", error->message);
+      g_error_free (error);
+      goto out;
+  }
+
+  g_main_loop_run (loop);
+
+  goa_identity_service_deactivate (service);
+
+  ret = 0;
+out:
+  g_object_unref (service);
+  g_main_loop_unref (loop);
+
+  return ret;
+}
-- 
1.8.3.1