diff --git a/SOURCES/add-smartcard-support.patch b/SOURCES/add-smartcard-support.patch new file mode 100644 index 0000000..a763047 --- /dev/null +++ b/SOURCES/add-smartcard-support.patch @@ -0,0 +1,2373 @@ +From 0ccab22ee39bd98b63d31c7457d91778b98080bf Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 22 Oct 2014 11:06:19 -0400 +Subject: [PATCH 1/3] kerberos: zero initialize operation structure + +We need zero initialize the structure so we don't end up +freeing unused members when cleaning up the operation. +--- + src/goaidentity/goakerberosidentitymanager.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c +index 7cc90b9..c4da420 100644 +--- a/src/goaidentity/goakerberosidentitymanager.c ++++ b/src/goaidentity/goakerberosidentitymanager.c +@@ -102,61 +102,61 @@ typedef struct + GoaKerberosIdentityManager *manager; + GoaIdentity *identity; + } IdentitySignalWork; + + static GoaIdentityManager *goa_kerberos_identity_manager_singleton; + + static void identity_manager_interface_init (GoaIdentityManagerInterface * + interface); + static void initable_interface_init (GInitableIface *interface); + + static void on_identity_expired (GoaIdentity *identity, + GoaKerberosIdentityManager *self); + + G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager, + goa_kerberos_identity_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER, + identity_manager_interface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_interface_init)); + #define FALLBACK_POLLING_INTERVAL 5 + + static Operation * + operation_new (GoaKerberosIdentityManager *self, + GCancellable *cancellable, + OperationType type, + GSimpleAsyncResult *result) + { + Operation *operation; + +- operation = g_slice_new (Operation); ++ operation = g_slice_new0 (Operation); + + operation->manager = self; + operation->type = type; + + if (cancellable == NULL) + cancellable = g_cancellable_new (); + else + g_object_ref (cancellable); + operation->cancellable = cancellable; + + if (result != NULL) + g_object_ref (result); + operation->result = result; + + operation->identity = NULL; + + return operation; + } + + static void + operation_free (Operation *operation) + { + g_clear_object (&operation->cancellable); + + if (operation->type != OPERATION_TYPE_SIGN_IN && + operation->type != OPERATION_TYPE_GET_IDENTITY) + g_clear_object (&operation->identity); + else + g_clear_pointer (&operation->identifier, g_free); + +-- +1.8.3.1 + + +From 8e36b5743bf1e20636a67f406f4937ccc132e50b Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 21 Oct 2014 10:38:17 -0400 +Subject: [PATCH 2/3] ticketing: add new details property + +This commit adds a new "details" variant for attaching metadata +about tickets getting requested via the ticketing interface. + +This will give the kerberos account provider a place to tuck away +kerberos-specific preauthentication configuration for the tickets +associated with smartcard backed kerberos accounts. +--- + data/dbus-interfaces.xml | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/data/dbus-interfaces.xml b/data/dbus-interfaces.xml +index fa4101f..e4d39c4 100644 +--- a/data/dbus-interfaces.xml ++++ b/data/dbus-interfaces.xml +@@ -604,40 +604,42 @@ + + + + + + + + + ++ ++ + + + + + +-- +1.8.3.1 + + +From 2cf3c36818fa995791d99c6512b258099cec2879 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Tue, 21 Oct 2014 10:46:50 -0400 +Subject: [PATCH 3/3] kerberos: support refreshing smartcard authenticated + kerberos tickets + +Right now gnome-online-accounts doesn't manager smartcard based +kerberos credentials very well. The sign-in button just fails. + +This commit adds support for tickets granted through smartcards. + +Note, at the moment we don't provide a way to add new smartcard +based accounts, merely, manage existing ones that are added implicitly +by logging in with a smartcard, or by explicit kinit. +--- + src/goabackend/goakerberosprovider.c | 43 +++++++++++++++++ + src/goaidentity/goaidentitymanager.c | 2 + + src/goaidentity/goaidentitymanager.h | 2 + + src/goaidentity/goaidentityservice.c | 19 +++++++- + src/goaidentity/goakerberosidentity.c | 71 +++++++++++++++++++++++++++- + src/goaidentity/goakerberosidentity.h | 2 + + src/goaidentity/goakerberosidentitymanager.c | 14 ++++-- + 7 files changed, 146 insertions(+), 7 deletions(-) + +diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c +index 7fe77f6..b125914 100644 +--- a/src/goabackend/goakerberosprovider.c ++++ b/src/goabackend/goakerberosprovider.c +@@ -345,93 +345,100 @@ ensure_object_manager (GoaKerberosProvider *self, + "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); +@@ -513,82 +520,88 @@ on_identity_manager_ensured_for_sign_in (GoaKerberosProvider *self, + + 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; + } +@@ -666,128 +679,141 @@ look_up_identity (GoaKerberosProvider *self, + (GAsyncReadyCallback) + on_object_manager_ensured_for_look_up, + operation_result); + } + + static void + on_account_signed_in (GoaProvider *provider, + GAsyncResult *result, + SignInRequest *request) + { + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + &request->error)) + { + g_main_loop_quit (request->loop); + return; + } + + g_main_loop_quit (request->loop); + } + + static gboolean + get_ticket_sync (GoaKerberosProvider *self, + GoaObject *object, + gboolean is_interactive, + GCancellable *cancellable, + GError **error) + { + GVariant *credentials; + GError *lookup_error; + GoaAccount *account; ++ GoaTicketing *ticketing; ++ GVariant *details; + const char *identifier; + const char *password; ++ const char *preauth_source; + SignInRequest request; + 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); + + if (request.error != NULL) + { + g_propagate_error (error, request.error); + goto out; + } + + ret = TRUE; + out: ++ g_clear_object (&ticketing); ++ + 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 + goa_utils_keyfile_remove_key (account, "SessionId"); +@@ -831,69 +857,79 @@ build_object (GoaProvider *provider, + GDBusConnection *connection, + gboolean just_added, + GError **error) + { + GoaAccount *account; + GoaTicketing *ticketing; + gboolean ticketing_enabled; + gboolean ret; + + ticketing = NULL; + ret = FALSE; + + if (!GOA_PROVIDER_CLASS (goa_kerberos_provider_parent_class)->build_object (provider, + object, + key_file, + group, + connection, + just_added, + error)) + goto out; + + account = goa_object_get_account (GOA_OBJECT (object)); + + ticketing = goa_object_get_ticketing (GOA_OBJECT (object)); + ticketing_enabled = g_key_file_get_boolean (key_file, group, "TicketingEnabled", NULL); + + if (ticketing_enabled) + { + if (ticketing == NULL) + { ++ char *preauthentication_source; ++ GVariantBuilder details; ++ + ticketing = goa_ticketing_skeleton_new (); + + g_signal_connect (ticketing, + "handle-get-ticket", + G_CALLBACK (on_handle_get_ticket), + NULL); + + goa_object_skeleton_set_ticketing (object, ticketing); + ++ g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); ++ ++ preauthentication_source = g_key_file_get_string (key_file, group, "PreauthenticationSource", NULL); ++ if (preauthentication_source) ++ g_variant_builder_add (&details, "{ss}", "preauthentication-source", preauthentication_source); ++ ++ g_object_set (G_OBJECT (ticketing), "details", g_variant_builder_end (&details), NULL); + } + } + else if (ticketing != NULL) + { + goa_object_skeleton_set_ticketing (object, NULL); + } + + if (just_added) + { + goa_account_set_ticketing_disabled (account, !ticketing_enabled); + + g_signal_connect (account, + "notify::is-temporary", + G_CALLBACK (notify_is_temporary_cb), + connection); + + g_signal_connect (account, + "notify::ticketing-disabled", + G_CALLBACK (goa_util_account_notify_property_cb), + "TicketingEnabled"); + } + + ret = TRUE; + + out: + g_clear_object (&ticketing); + + return ret; + } + +@@ -1266,99 +1302,106 @@ on_initial_sign_in_done (GoaKerberosProvider *self, + */ + g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&builder, + "{sv}", + "password", + g_variant_new_string (password)); + + error = NULL; + goa_utils_store_credentials_for_object_sync (GOA_PROVIDER (self), + object, + g_variant_builder_end (&builder), + NULL, + NULL); + } + } + + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); + } + + static void + on_system_prompt_answered_for_initial_sign_in (GcrPrompt *prompt, + GAsyncResult *result, + GSimpleAsyncResult *operation_result) + { + GoaKerberosProvider *self; + GCancellable *cancellable; + GError *error; + const char *principal; + const char *password; ++ const char *preauth_source; + GcrSecretExchange *secret_exchange; + + self = GOA_KERBEROS_PROVIDER (g_async_result_get_source_object (G_ASYNC_RESULT (operation_result))); + principal = g_object_get_data (G_OBJECT (operation_result), "principal"); + cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable"); + ++ /* We currently don't prompt the user to choose a preauthentication source during initial sign in ++ * so we assume there's no preauthentication source ++ */ ++ preauth_source = NULL; ++ + error = NULL; + password = gcr_prompt_password_finish (prompt, result, &error); + + if (password == NULL) + { + gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL); + + if (error != NULL) + g_simple_async_result_take_error (operation_result, error); + else + g_cancellable_cancel (cancellable); + + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); + return; + } + + secret_exchange = gcr_system_prompt_get_secret_exchange (GCR_SYSTEM_PROMPT (prompt)); + g_object_set_data_full (G_OBJECT (operation_result), + "secret-exchange", + g_object_ref (secret_exchange), + (GDestroyNotify) + g_object_unref); + + g_object_set_data (G_OBJECT (operation_result), + "remember-password", + GINT_TO_POINTER (gcr_prompt_get_choice_chosen (prompt))); + + gcr_system_prompt_close (GCR_SYSTEM_PROMPT (prompt), NULL, NULL); + + sign_in_identity (self, + principal, + password, ++ preauth_source, + cancellable, + (GAsyncReadyCallback) + on_initial_sign_in_done, + operation_result); + } + + static void + on_system_prompt_open_for_initial_sign_in (GcrSystemPrompt *system_prompt, + GAsyncResult *result, + GSimpleAsyncResult *operation_result) + { + GCancellable *cancellable; + GcrPrompt *prompt; + GError *error; + + cancellable = g_object_get_data (G_OBJECT (operation_result), "cancellable"); + error = NULL; + prompt = gcr_system_prompt_open_finish (result, &error); + + if (prompt == NULL) + { + g_simple_async_result_take_error (operation_result, error); + + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); + + return; + } + + gcr_prompt_set_title (prompt, _("Log In to Realm")); +diff --git a/src/goaidentity/goaidentitymanager.c b/src/goaidentity/goaidentitymanager.c +index b71fb23..8fa7b22 100644 +--- a/src/goaidentity/goaidentitymanager.c ++++ b/src/goaidentity/goaidentitymanager.c +@@ -178,70 +178,72 @@ goa_identity_manager_list_identities_finish (GoaIdentityManager *self, + result, + error); + } + + void + goa_identity_manager_renew_identity (GoaIdentityManager *self, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity (self, + identity, + cancellable, + callback, + user_data); + } + + void + goa_identity_manager_renew_identity_finish (GoaIdentityManager *self, + GAsyncResult *result, + GError **error) + { + GOA_IDENTITY_MANAGER_GET_IFACE (self)->renew_identity_finish (self, result, error); + } + + void + goa_identity_manager_sign_identity_in (GoaIdentityManager *self, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in (self, + identifier, + initial_password, ++ preauth_source, + flags, + inquiry_func, + inquiry_data, + cancellable, + callback, + user_data); + } + + GoaIdentity * + goa_identity_manager_sign_identity_in_finish (GoaIdentityManager *self, + GAsyncResult *result, + GError **error) + { + return GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_in_finish (self, + result, + error); + } + + void + goa_identity_manager_sign_identity_out (GoaIdentityManager *self, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GOA_IDENTITY_MANAGER_GET_IFACE (self)->sign_identity_out (self, + identity, + cancellable, + callback, + user_data); +diff --git a/src/goaidentity/goaidentitymanager.h b/src/goaidentity/goaidentitymanager.h +index 5b60c24..05e6ca8 100644 +--- a/src/goaidentity/goaidentitymanager.h ++++ b/src/goaidentity/goaidentitymanager.h +@@ -54,60 +54,61 @@ struct _GoaIdentityManagerInterface + GoaIdentity *identity); + void (* identity_refreshed) (GoaIdentityManager *identity_manager, + GoaIdentity *identity); + void (* identity_needs_renewal) (GoaIdentityManager *identity_manager, + GoaIdentity *identity); + void (* identity_expiring) (GoaIdentityManager *identity_manager, + GoaIdentity *identity); + void (* identity_expired) (GoaIdentityManager *identity_manager, + GoaIdentity *identity); + + /* Virtual Functions */ + void (* get_identity) (GoaIdentityManager *identity_manager, + const char *identifier, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GoaIdentity * (* get_identity_finish) (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + void (* list_identities) (GoaIdentityManager *identity_manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GList * (* list_identities_finish) (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* sign_identity_in) (GoaIdentityManager *identity_manager, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GoaIdentity * (* sign_identity_in_finish) (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* sign_identity_out) (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void (* sign_identity_out_finish) (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void (* renew_identity) (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void (* renew_identity_finish) (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + char * (* name_identity) (GoaIdentityManager *identity_manager, + GoaIdentity *identity); +@@ -117,60 +118,61 @@ typedef enum + { + GOA_IDENTITY_MANAGER_ERROR_INITIALIZING, + GOA_IDENTITY_MANAGER_ERROR_IDENTITY_NOT_FOUND, + GOA_IDENTITY_MANAGER_ERROR_CREATING_IDENTITY, + GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS, + GOA_IDENTITY_MANAGER_ERROR_UNSUPPORTED_CREDENTIALS + } GoaIdentityManagerError; + + GType goa_identity_manager_get_type (void); + GQuark goa_identity_manager_error_quark (void); + + void goa_identity_manager_get_identity (GoaIdentityManager *identity_manager, + const char *identifier, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GoaIdentity *goa_identity_manager_get_identity_finish (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + void goa_identity_manager_list_identities (GoaIdentityManager *identity_manager, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GList *goa_identity_manager_list_identities_finish (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void goa_identity_manager_sign_identity_in (GoaIdentityManager *identity_manager, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + GoaIdentity *goa_identity_manager_sign_identity_in_finish (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void goa_identity_manager_sign_identity_out (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void goa_identity_manager_sign_identity_out_finish (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + void goa_identity_manager_renew_identity (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + void goa_identity_manager_renew_identity_finish (GoaIdentityManager *identity_manager, + GAsyncResult *result, + GError **error); + + char *goa_identity_manager_name_identity (GoaIdentityManager *identity_manager, + GoaIdentity *identity); +diff --git a/src/goaidentity/goaidentityservice.c b/src/goaidentity/goaidentityservice.c +index d51d6bd..b570e09 100644 +--- a/src/goaidentity/goaidentityservice.c ++++ b/src/goaidentity/goaidentityservice.c +@@ -38,60 +38,61 @@ + #include "goaidentityutils.h" + + #include "goakerberosidentitymanager.h" + #include "goalogging.h" + + struct _GoaIdentityServicePrivate + { + GDBusConnection *connection; + GDBusObjectManagerServer *object_manager_server; + guint bus_id; + + GoaIdentityManager *identity_manager; + + GHashTable *watched_client_connections; + GHashTable *key_holders; + GHashTable *pending_temporary_account_results; + + /* FIXME: This is a little ucky, since the goa service + * is in process, we should able to use direct calls. + */ + GoaClient *client; + GoaManager *accounts_manager; + }; + + static void identity_service_manager_interface_init (GoaIdentityServiceManagerIface *interface); + + static void + sign_in (GoaIdentityService *self, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + + G_DEFINE_TYPE_WITH_CODE (GoaIdentityService, + goa_identity_service, + GOA_IDENTITY_SERVICE_TYPE_MANAGER_SKELETON, + G_IMPLEMENT_INTERFACE (GOA_IDENTITY_SERVICE_TYPE_MANAGER, + identity_service_manager_interface_init)); + + static char * + get_object_path_for_identity (GoaIdentityService *self, + GoaIdentity *identity) + { + const char *identifier; + char *escaped_identifier; + char *object_path; + + identifier = goa_identity_get_identifier (identity); + escaped_identifier = goa_identity_utils_escape_object_path (identifier, + strlen (identifier)); + object_path = g_strdup_printf ("/org/gnome/Identity/Identities/%s", escaped_identifier); + + g_free (escaped_identifier); + return object_path; + } + + static char * + export_identity (GoaIdentityService *self, +@@ -290,151 +291,158 @@ ensure_account_credentials (GoaIdentityService *self, + on_credentials_ensured, + self); + } + + static void + on_sign_in_handled (GoaIdentityService *self, + GAsyncResult *result, + GDBusMethodInvocation *invocation) + { + GError *error = NULL; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + } + else + { + const char *object_path; + + object_path = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + goa_identity_service_manager_complete_sign_in (GOA_IDENTITY_SERVICE_MANAGER (self), + invocation, + object_path); + } + } + + static void + read_sign_in_details (GoaIdentityServiceManager *manager, + GVariant *details, + GoaIdentitySignInFlags *flags, +- char **secret_key) ++ char **secret_key, ++ char **preauth_source) + { + GVariantIter iter; + char *key; + char *value; + + *flags = GOA_IDENTITY_SIGN_IN_FLAGS_NONE; + g_variant_iter_init (&iter, details); + while (g_variant_iter_loop (&iter, "{ss}", &key, &value)) + { + if (g_strcmp0 (key, "initial-password") == 0) + *secret_key = g_strdup (value); ++ else if (g_strcmp0 (key, "preauthentication-source") == 0) ++ *preauth_source = g_strdup (value); + else if (g_strcmp0 (key, "disallow-renewal") == 0 + && g_strcmp0 (value, "true") == 0) + *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL; + else if (g_strcmp0 (key, "disallow-forwarding") == 0 + && g_strcmp0 (value, "true") == 0) + *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING; + else if (g_strcmp0 (key, "disallow-proxying") == 0 + && g_strcmp0 (value, "true") == 0) + *flags |= GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING; + } + } + + static gboolean + goa_identity_service_handle_sign_in (GoaIdentityServiceManager *manager, + GDBusMethodInvocation *invocation, + const char *identifier, + GVariant *details) + { + GoaIdentityService *self = GOA_IDENTITY_SERVICE (manager); + GSimpleAsyncResult *operation_result; + GoaIdentitySignInFlags flags; + char *secret_key; ++ char *preauth_source; + gconstpointer initial_password; + GCancellable *cancellable; + + secret_key = NULL; ++ preauth_source = NULL; + initial_password = NULL; + +- read_sign_in_details (manager, details, &flags, &secret_key); ++ read_sign_in_details (manager, details, &flags, &secret_key, &preauth_source); + + if (secret_key != NULL) + { + GcrSecretExchange *secret_exchange; + + secret_exchange = g_hash_table_lookup (self->priv->key_holders, + g_dbus_method_invocation_get_sender (invocation)); + + if (secret_exchange == NULL) + { + g_free (secret_key); + g_dbus_method_invocation_return_error (invocation, + GOA_IDENTITY_MANAGER_ERROR, + GOA_IDENTITY_MANAGER_ERROR_ACCESSING_CREDENTIALS, + _("initial secret passed before secret key exchange")); + return TRUE; + } + + gcr_secret_exchange_receive (secret_exchange, secret_key); + g_free (secret_key); + + initial_password = gcr_secret_exchange_get_secret (secret_exchange, NULL); + } + + operation_result = g_simple_async_result_new (G_OBJECT (self), + (GAsyncReadyCallback) + on_sign_in_handled, + g_object_ref (invocation), + g_strdup (identifier)); + cancellable = g_cancellable_new (); + g_object_set_data (G_OBJECT (operation_result), + "cancellable", + cancellable); + g_object_set_data (G_OBJECT (operation_result), + "initial-password", + (gpointer) + initial_password); + g_object_set_data (G_OBJECT (operation_result), + "flags", + GINT_TO_POINTER ((int) flags)); + + sign_in (self, + identifier, + initial_password, ++ preauth_source, + flags, + cancellable, + (GAsyncReadyCallback) + on_sign_in_done, + operation_result); + ++ g_free (preauth_source); + g_object_unref (cancellable); + + return TRUE; + } + + static void + on_sign_out_handled (GoaIdentityService *self, + GAsyncResult *result, + GDBusMethodInvocation *invocation) + { + GError *error; + + error = NULL; + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), &error)) + g_dbus_method_invocation_take_error (invocation, error); + else + goa_identity_service_manager_complete_sign_out (GOA_IDENTITY_SERVICE_MANAGER (self), + invocation); + } + + static void + on_identity_signed_out (GoaIdentityManager *manager, + GAsyncResult *result, + GSimpleAsyncResult *operation_result) + { + GoaIdentityService *self; + GError *error; + GoaIdentity *identity; + const char *identifier; + GoaObject *object; +@@ -846,117 +854,122 @@ on_account_added (GoaManager *manager, + return; + } + + if (object_path != NULL && object_path[0] != '\0') + { + goa_debug ("Created account for identity with object path %s", object_path); + + object_manager = goa_client_get_object_manager (self->priv->client); + object = GOA_OBJECT (g_dbus_object_manager_get_object (object_manager, + object_path)); + g_free (object_path); + } + + if (object == NULL) + g_simple_async_result_set_op_res_gpointer (operation_result, NULL, NULL); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + object, + (GDestroyNotify) + g_object_unref); + + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); + } + + static void + add_temporary_account (GoaIdentityService *self, + GoaIdentity *identity) + { + char *realm; ++ char *preauth_source; + const char *principal; + char *principal_for_display; + GSimpleAsyncResult *operation_result; + GVariantBuilder credentials; + GVariantBuilder details; + GoaObject *object; + + principal = goa_identity_get_identifier (identity); + + object = g_hash_table_lookup (self->priv->pending_temporary_account_results, + principal); + + if (object != NULL) + { + goa_debug ("GoaIdentityService: would add temporary identity %s, but it's already pending", principal); + return; + } + + goa_debug ("GoaIdentityService: adding temporary identity %s", principal); + + /* If there's no account for this identity then create a temporary one. + */ + principal_for_display = goa_identity_manager_name_identity (self->priv->identity_manager, + identity); + + realm = goa_kerberos_identity_get_realm_name (GOA_KERBEROS_IDENTITY (identity)); ++ preauth_source = goa_kerberos_identity_get_preauthentication_source (GOA_KERBEROS_IDENTITY (identity)); + + g_variant_builder_init (&credentials, G_VARIANT_TYPE_VARDICT); + + g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); + g_variant_builder_add (&details, "{ss}", "Realm", realm); + g_variant_builder_add (&details, "{ss}", "IsTemporary", "true"); ++ if (preauth_source != NULL) ++ g_variant_builder_add (&details, "{ss}", "PreauthenticationSource", preauth_source); + g_variant_builder_add (&details, "{ss}", "TicketingEnabled", "true"); + + + goa_debug ("GoaIdentityService: asking to sign back in"); + + operation_result = g_simple_async_result_new (G_OBJECT (self), + (GAsyncReadyCallback) + on_temporary_account_created_for_identity, + identity, + add_temporary_account); + g_hash_table_insert (self->priv->pending_temporary_account_results, + g_strdup (principal), + g_object_ref (operation_result)); + + goa_manager_call_add_account (self->priv->accounts_manager, + "kerberos", + principal, + principal_for_display, + g_variant_builder_end (&credentials), + g_variant_builder_end (&details), + NULL, + (GAsyncReadyCallback) + on_account_added, + operation_result); + g_free (realm); ++ g_free (preauth_source); + g_free (principal_for_display); + } + + static void + on_identity_added (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GoaIdentityService *self) + { + GoaObject *object; + const char *identifier; + + export_identity (self, identity); + + identifier = goa_identity_get_identifier (identity); + + object = find_object_with_principal (self, identifier, FALSE); + + if (object == NULL) + add_temporary_account (self, identity); + } + + static void + on_identity_removed (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GoaIdentityService *self) + { + GoaObject *object; + const char *identifier; + + identifier = goa_identity_get_identifier (identity); +@@ -1239,87 +1252,89 @@ on_identity_inquiry (GoaIdentityInquiry *inquiry, + request = system_prompt_open_request_new (self, inquiry, cancellable); + gcr_system_prompt_open_async (-1, + cancellable, + (GAsyncReadyCallback) + on_system_prompt_open, + request); + } + + static void + cancel_sign_in (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GSimpleAsyncResult *operation_result) + { + GoaIdentity *operation_identity; + + operation_identity = g_simple_async_result_get_source_tag (operation_result); + if (operation_identity == identity) + { + GCancellable *cancellable; + + cancellable = g_object_get_data (G_OBJECT (operation_result), + "cancellable"); + g_cancellable_cancel (cancellable); + } + } + + static void + sign_in (GoaIdentityService *self, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GSimpleAsyncResult *operation_result; + + goa_debug ("GoaIdentityService: asking to sign in"); + + operation_result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + NULL); + g_simple_async_result_set_check_cancellable (operation_result, cancellable); + + g_object_set_data (G_OBJECT (operation_result), + "cancellable", + cancellable); + g_signal_connect_object (G_OBJECT (self->priv->identity_manager), + "identity-refreshed", + G_CALLBACK (cancel_sign_in), + operation_result, + 0); + + goa_identity_manager_sign_identity_in (self->priv->identity_manager, + identifier, + initial_password, ++ preauth_source, + flags, + (GoaIdentityInquiryFunc) + on_identity_inquiry, + self, + cancellable, + (GAsyncReadyCallback) + on_identity_signed_in, + operation_result); + } + + static void + on_identity_expiring (GoaIdentityManager *identity_manager, + GoaIdentity *identity, + GoaIdentityService *self) + { + const char *principal; + GoaObject *object; + + principal = goa_identity_get_identifier (identity); + + goa_debug ("GoaIdentityService: identity %s expiring", principal); + + object = find_object_with_principal (self, principal, TRUE); + + if (object == NULL) + return; + + ensure_account_credentials (self, object); + } + +diff --git a/src/goaidentity/goakerberosidentity.c b/src/goaidentity/goakerberosidentity.c +index c3a65f6..7641102 100644 +--- a/src/goaidentity/goakerberosidentity.c ++++ b/src/goaidentity/goakerberosidentity.c +@@ -25,60 +25,62 @@ + #include "goaidentity.h" + #include "goakerberosidentity.h" + #include "goakerberosidentityinquiry.h" + #include "goaalarm.h" + #include "goalogging.h" + + #include + #include + #include + + #include + #include + #include + + typedef enum + { + VERIFICATION_LEVEL_UNVERIFIED, + VERIFICATION_LEVEL_ERROR, + VERIFICATION_LEVEL_EXISTS, + VERIFICATION_LEVEL_SIGNED_IN + } VerificationLevel; + + struct _GoaKerberosIdentityPrivate + { + krb5_context kerberos_context; + krb5_ccache credentials_cache; + + char *identifier; + guint identifier_idle_id; + ++ char *preauth_identity_source; ++ + krb5_timestamp expiration_time; + guint expiration_time_idle_id; + + GoaAlarm *expiration_alarm; + GoaAlarm *expiring_alarm; + GoaAlarm *renewal_alarm; + + VerificationLevel cached_verification_level; + guint is_signed_in_idle_id; + }; + + enum + { + EXPIRING, + EXPIRED, + UNEXPIRED, + NEEDS_RENEWAL, + NEEDS_REFRESH, + NUMBER_OF_SIGNALS, + }; + + enum + { + PROP_0, + PROP_IDENTIFIER, + PROP_IS_SIGNED_IN, + PROP_EXPIRATION_TIMESTAMP + }; + + static guint signals[NUMBER_OF_SIGNALS] = { 0 }; +@@ -86,60 +88,62 @@ 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); ++ 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) + { + case PROP_IDENTIFIER: +@@ -385,108 +389,152 @@ goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self) + krb5_principal principal; + krb5_error_code error_code; + krb5_data *realm; + char *realm_name; + + 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); + return NULL; + } + + realm = krb5_princ_realm (self->priv->kerberos_context, principal); + realm_name = g_strndup (realm->data, realm->length); + krb5_free_principal (self->priv->kerberos_context, principal); + + return realm_name; + } + ++char * ++goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self) ++{ ++ return g_strdup (self->priv->preauth_identity_source); ++} ++ + static const char * + goa_kerberos_identity_get_identifier (GoaIdentity *identity) + { + GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (identity); + + return self->priv->identifier; + } + + static gboolean + credentials_validate_existence (GoaKerberosIdentity *self, + krb5_principal principal, krb5_creds * credentials) + { + /* Checks if default principal associated with the cache has a valid + * ticket granting ticket in the passed in credentials + */ + + if (krb5_is_config_principal (self->priv->kerberos_context, credentials->server)) + return FALSE; + + /* looking for the krbtgt / REALM pair, so it should be exactly 2 items */ + if (krb5_princ_size (self->priv->kerberos_context, credentials->server) != 2) + return FALSE; + + if (!krb5_realm_compare (self->priv->kerberos_context, + credentials->server, principal)) + { + /* credentials are from some other realm */ + return FALSE; + } + + if (strncmp (credentials->server->data[0].data, + KRB5_TGS_NAME, credentials->server->data[0].length) != 0) + { + /* credentials aren't for ticket granting */ + return FALSE; + } + + if (credentials->server->data[1].length != principal->realm.length || + memcmp (credentials->server->data[1].data, + principal->realm.data, principal->realm.length) != 0) + { + /* credentials are for some other realm */ + return FALSE; + } + + return TRUE; + } + ++static gboolean ++snoop_preauth_identity_from_credentials (GoaKerberosIdentity *self, ++ krb5_creds *credentials, ++ char **identity_source) ++{ ++ GRegex *regex; ++ GMatchInfo *match_info = NULL; ++ gboolean identity_source_exposed = FALSE; ++ ++ if (!krb5_is_config_principal (self->priv->kerberos_context, credentials->server)) ++ return FALSE; ++ ++ regex = g_regex_new ("\"X509_user_identity\":\"(?P[^\"]*)\"", ++ G_REGEX_MULTILINE | G_REGEX_CASELESS | G_REGEX_RAW, ++ 0, ++ NULL); ++ ++ if (regex == NULL) ++ return FALSE; ++ ++ g_regex_match_full (regex, credentials->ticket.data, credentials->ticket.length, 0, 0, &match_info, NULL); ++ ++ if (match_info != NULL && g_match_info_matches (match_info)) ++ { ++ if (identity_source) ++ { ++ g_free (*identity_source); ++ *identity_source = g_match_info_fetch_named (match_info, "identity_source"); ++ } ++ identity_source_exposed = TRUE; ++ } ++ ++ g_match_info_free (match_info); ++ g_regex_unref (regex); ++ ++ return identity_source_exposed; ++} ++ + static krb5_timestamp + get_current_time (GoaKerberosIdentity *self) + { + krb5_timestamp current_time; + krb5_error_code error_code; + + error_code = krb5_timeofday (self->priv->kerberos_context, ¤t_time); + + if (error_code != 0) + { + const char *error_message; + + error_message = + krb5_get_error_message (self->priv->kerberos_context, error_code); + goa_debug ("GoaKerberosIdentity: Error getting current time: %s", error_message); + krb5_free_error_message (self->priv->kerberos_context, error_message); + return 0; + } + + return current_time; + } + + typedef struct + { + GoaKerberosIdentity *self; + guint *idle_id; + const char *property_name; + } NotifyRequest; + + static void +@@ -537,60 +585,61 @@ set_expiration_time (GoaKerberosIdentity *self, + if (self->priv->expiration_time != expiration_time) + { + self->priv->expiration_time = expiration_time; + queue_notify (self, + &self->priv->expiration_time_idle_id, + "expiration-timestamp"); + } + } + + static gboolean + credentials_are_expired (GoaKerberosIdentity *self, + krb5_creds *credentials) + { + krb5_timestamp current_time; + + current_time = get_current_time (self); + + set_expiration_time (self, MAX (credentials->times.endtime, + self->priv->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) + { + if (error_code == KRB5_CC_END || error_code == KRB5_FCC_NOFILE) + return VERIFICATION_LEVEL_UNVERIFIED; + + 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; + } +@@ -599,60 +648,64 @@ verify_identity (GoaKerberosIdentity *self, + 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; + 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; +@@ -900,61 +953,61 @@ reset_alarms (GoaKerberosIdentity *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); + clear_alarm_and_unref_on_idle (self, &self->priv->expiration_alarm); + } + + static gboolean + goa_kerberos_identity_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) + { + GoaKerberosIdentity *self = GOA_KERBEROS_IDENTITY (initable); + GError *verification_error; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + if (self->priv->identifier == NULL) + { + self->priv->identifier = get_identifier (self, error); + + if (self->priv->identifier != NULL) + queue_notify (self, &self->priv->identifier_idle_id, "identifier"); + } + + verification_error = NULL; + self->priv->cached_verification_level = +- verify_identity (self, &verification_error); ++ verify_identity (self, &self->priv->preauth_identity_source, &verification_error); + + switch (self->priv->cached_verification_level) + { + case VERIFICATION_LEVEL_EXISTS: + case VERIFICATION_LEVEL_SIGNED_IN: + reset_alarms (self); + + queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in"); + return TRUE; + + case VERIFICATION_LEVEL_UNVERIFIED: + return TRUE; + + case VERIFICATION_LEVEL_ERROR: + if (verification_error != NULL) + { + g_propagate_error (error, verification_error); + return FALSE; + } + default: + g_set_error (error, + GOA_IDENTITY_ERROR, + GOA_IDENTITY_ERROR_VERIFYING, + _("No associated identification found")); + return FALSE; + + } + } + + static void +@@ -1105,60 +1158,61 @@ sign_in_operation_new (GoaKerberosIdentity *identity, + { + SignInOperation *operation; + + operation = g_slice_new0 (SignInOperation); + operation->identity = g_object_ref (identity); + operation->inquiry_func = inquiry_func; + operation->inquiry_data = inquiry_data; + operation->destroy_notify = destroy_notify; + + if (cancellable == NULL) + operation->cancellable = g_cancellable_new (); + else + operation->cancellable = g_object_ref (cancellable); + + return operation; + } + + static void + sign_in_operation_free (SignInOperation *operation) + { + g_object_unref (operation->identity); + g_object_unref (operation->cancellable); + + g_slice_free (SignInOperation, operation); + } + + gboolean + goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, + const char *principal_name, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GDestroyNotify destroy_notify, + GCancellable *cancellable, + GError **error) + { + SignInOperation *operation; + krb5_principal principal; + krb5_error_code error_code; + krb5_creds new_credentials; + krb5_get_init_creds_opt *options; + krb5_deltat start_time; + char *service_name; + gboolean signed_in; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + error_code = krb5_get_init_creds_opt_alloc (self->priv->kerberos_context, + &options); + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_ALLOCATING_CREDENTIALS, + error_code, "%k"); + if (destroy_notify) + destroy_notify (inquiry_data); + return FALSE; +@@ -1176,60 +1230,67 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, + { + g_free (self->priv->identifier); + self->priv->identifier = g_strdup (principal_name); + } + + error_code = krb5_parse_name (self->priv->kerberos_context, + principal_name, + &principal); + + if (error_code != 0) + { + set_error_from_krb5_error_code (self, + error, + GOA_IDENTITY_ERROR_PARSING_IDENTIFIER, + error_code, + "%k"); + if (destroy_notify) + destroy_notify (inquiry_data); + return FALSE; + } + + if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_FORWARDING) == 0) + krb5_get_init_creds_opt_set_forwardable (options, TRUE); + + if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_PROXYING) == 0) + krb5_get_init_creds_opt_set_proxiable (options, TRUE); + + if ((flags & GOA_IDENTITY_SIGN_IN_FLAGS_DISALLOW_RENEWAL) == 0) + krb5_get_init_creds_opt_set_renew_life (options, G_MAXINT); + ++ if (preauth_source != NULL) ++ { ++ krb5_get_init_creds_opt_set_pa (self->priv->kerberos_context, ++ options, ++ "X509_user_identity", preauth_source); ++ } ++ + /* Poke glibc in case the network changed + */ + res_init (); + + start_time = 0; + service_name = NULL; + error_code = krb5_get_init_creds_password (self->priv->kerberos_context, + &new_credentials, + principal, + (char *) + initial_password, + (krb5_prompter_fct) + on_kerberos_inquiry, + operation, + start_time, + service_name, + options); + + if (error_code == KRB5_LIBOS_PWDINTR) + g_cancellable_cancel (operation->cancellable); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + { + if (destroy_notify) + destroy_notify (inquiry_data); + sign_in_operation_free (operation); + + krb5_free_principal (self->priv->kerberos_context, principal); + goto done; + } +@@ -1266,79 +1327,85 @@ goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, + goa_debug ("GoaKerberosIdentity: identity signed in"); + signed_in = TRUE; + done: + + return signed_in; + } + + static void + update_identifier (GoaKerberosIdentity *self, GoaKerberosIdentity *new_identity) + { + char *new_identifier; + + new_identifier = get_identifier (self, NULL); + if (g_strcmp0 (self->priv->identifier, new_identifier) != 0) + { + g_free (self->priv->identifier); + self->priv->identifier = new_identifier; + queue_notify (self, &self->priv->identifier_idle_id, "identifier"); + } + else + { + g_free (new_identifier); + } + } + + void + goa_kerberos_identity_update (GoaKerberosIdentity *self, + GoaKerberosIdentity *new_identity) + { + VerificationLevel verification_level; ++ char *preauth_identity_source = NULL; + + if (self->priv->credentials_cache != NULL) + krb5_cc_close (self->priv->kerberos_context, self->priv->credentials_cache); + + krb5_cc_dup (new_identity->priv->kerberos_context, + new_identity->priv->credentials_cache, + &self->priv->credentials_cache); + + G_LOCK (identity_lock); + update_identifier (self, new_identity); + G_UNLOCK (identity_lock); + +- verification_level = verify_identity (self, NULL); ++ verification_level = verify_identity (self, &preauth_identity_source, NULL); + + if (verification_level == VERIFICATION_LEVEL_SIGNED_IN) + reset_alarms (self); + else + clear_alarms (self); + ++ G_LOCK (identity_lock); ++ g_free (self->priv->preauth_identity_source); ++ self->priv->preauth_identity_source = preauth_identity_source; ++ G_UNLOCK (identity_lock); ++ + if (verification_level != self->priv->cached_verification_level) + { + if (self->priv->cached_verification_level == VERIFICATION_LEVEL_SIGNED_IN && + verification_level == VERIFICATION_LEVEL_EXISTS) + { + + G_LOCK (identity_lock); + self->priv->cached_verification_level = verification_level; + G_UNLOCK (identity_lock); + + g_signal_emit (G_OBJECT (self), signals[EXPIRED], 0); + } + else if (self->priv->cached_verification_level == VERIFICATION_LEVEL_EXISTS && + verification_level == VERIFICATION_LEVEL_SIGNED_IN) + { + + G_LOCK (identity_lock); + self->priv->cached_verification_level = verification_level; + G_UNLOCK (identity_lock); + + g_signal_emit (G_OBJECT (self), signals[UNEXPIRED], 0); + } + else + { + G_LOCK (identity_lock); + self->priv->cached_verification_level = verification_level; + G_UNLOCK (identity_lock); + } + queue_notify (self, &self->priv->is_signed_in_idle_id, "is-signed-in"); + } +diff --git a/src/goaidentity/goakerberosidentity.h b/src/goaidentity/goakerberosidentity.h +index 6e82835..e163016 100644 +--- a/src/goaidentity/goakerberosidentity.h ++++ b/src/goaidentity/goakerberosidentity.h +@@ -43,47 +43,49 @@ typedef enum _GoaKerberosIdentityDescriptionLevel + GoaKerberosIdentityDescriptionLevel; + + enum _GoaKerberosIdentityDescriptionLevel + { + GOA_KERBEROS_IDENTITY_DESCRIPTION_REALM, + GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_AND_REALM, + GOA_KERBEROS_IDENTITY_DESCRIPTION_USERNAME_ROLE_AND_REALM + }; + + struct _GoaKerberosIdentity + { + GObject parent; + + GoaKerberosIdentityPrivate *priv; + }; + + struct _GoaKerberosIdentityClass + { + GObjectClass parent_class; + }; + + GType goa_kerberos_identity_get_type (void); + + GoaIdentity *goa_kerberos_identity_new (krb5_context kerberos_context, + krb5_ccache cache, + GError **error); + + gboolean goa_kerberos_identity_sign_in (GoaKerberosIdentity *self, + const char *principal_name, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GDestroyNotify destroy_notify, + GCancellable *cancellable, + GError **error); + void goa_kerberos_identity_update (GoaKerberosIdentity *identity, + GoaKerberosIdentity *new_identity); + gboolean goa_kerberos_identity_renew (GoaKerberosIdentity *self, + GError **error); + gboolean goa_kerberos_identity_erase (GoaKerberosIdentity *self, + GError **error); + + char *goa_kerberos_identity_get_principal_name (GoaKerberosIdentity *self); + char *goa_kerberos_identity_get_realm_name (GoaKerberosIdentity *self); ++char *goa_kerberos_identity_get_preauthentication_source (GoaKerberosIdentity *self); + G_END_DECLS + #endif /* __GOA_KERBEROS_IDENTITY_H__ */ +diff --git a/src/goaidentity/goakerberosidentitymanager.c b/src/goaidentity/goakerberosidentitymanager.c +index c4da420..1fa0807 100644 +--- a/src/goaidentity/goakerberosidentitymanager.c ++++ b/src/goaidentity/goakerberosidentitymanager.c +@@ -59,60 +59,61 @@ struct _GoaKerberosIdentityManagerPrivate + volatile int pending_refresh_count; + + guint polling_timeout_id; + }; + + typedef enum + { + OPERATION_TYPE_REFRESH, + OPERATION_TYPE_GET_IDENTITY, + OPERATION_TYPE_LIST, + OPERATION_TYPE_RENEW, + OPERATION_TYPE_SIGN_IN, + OPERATION_TYPE_SIGN_OUT, + OPERATION_TYPE_STOP_JOB + } OperationType; + + typedef struct + { + GCancellable *cancellable; + GoaKerberosIdentityManager *manager; + OperationType type; + GSimpleAsyncResult *result; + GIOSchedulerJob *job; + union + { + GoaIdentity *identity; + struct + { + const char *identifier; + gconstpointer initial_password; ++ char *preauth_source; + GoaIdentitySignInFlags sign_in_flags; + GoaIdentityInquiry *inquiry; + GoaIdentityInquiryFunc inquiry_func; + gpointer inquiry_data; + GMutex inquiry_lock; + GCond inquiry_finished_condition; + volatile gboolean is_inquiring; + }; + }; + } Operation; + + typedef struct + { + GoaKerberosIdentityManager *manager; + GoaIdentity *identity; + } IdentitySignalWork; + + static GoaIdentityManager *goa_kerberos_identity_manager_singleton; + + static void identity_manager_interface_init (GoaIdentityManagerInterface * + interface); + static void initable_interface_init (GInitableIface *interface); + + static void on_identity_expired (GoaIdentity *identity, + GoaKerberosIdentityManager *self); + + G_DEFINE_TYPE_WITH_CODE (GoaKerberosIdentityManager, + goa_kerberos_identity_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GOA_TYPE_IDENTITY_MANAGER, +@@ -129,64 +130,68 @@ operation_new (GoaKerberosIdentityManager *self, + { + Operation *operation; + + operation = g_slice_new0 (Operation); + + operation->manager = self; + operation->type = type; + + if (cancellable == NULL) + cancellable = g_cancellable_new (); + else + g_object_ref (cancellable); + operation->cancellable = cancellable; + + if (result != NULL) + g_object_ref (result); + operation->result = result; + + operation->identity = NULL; + + return operation; + } + + static void + operation_free (Operation *operation) + { + g_clear_object (&operation->cancellable); + + if (operation->type != OPERATION_TYPE_SIGN_IN && + operation->type != OPERATION_TYPE_GET_IDENTITY) +- g_clear_object (&operation->identity); ++ { ++ g_clear_object (&operation->identity); ++ } + else +- g_clear_pointer (&operation->identifier, g_free); +- ++ { ++ g_clear_pointer (&operation->identifier, g_free); ++ g_clear_pointer (&operation->preauth_source, g_free); ++ } + g_clear_object (&operation->result); + + g_slice_free (Operation, operation); + } + + static void + schedule_refresh (GoaKerberosIdentityManager *self) + { + Operation *operation; + + g_atomic_int_inc (&self->priv->pending_refresh_count); + + operation = operation_new (self, NULL, OPERATION_TYPE_REFRESH, NULL); + g_async_queue_push (self->priv->pending_operations, operation); + } + + static IdentitySignalWork * + identity_signal_work_new (GoaKerberosIdentityManager *self, + GoaIdentity *identity) + { + IdentitySignalWork *work; + + work = g_slice_new (IdentitySignalWork); + work->manager = self; + work->identity = g_object_ref (identity); + + return work; + } + + static void +@@ -841,60 +846,61 @@ sign_in_identity (GoaKerberosIdentityManager *self, + _("Could not create credential cache for identity")); + g_simple_async_result_set_op_res_gpointer (operation->result, NULL, NULL); + return; + } + + identity = goa_kerberos_identity_new (self->priv->kerberos_context, + credentials_cache, + &error); + krb5_cc_close (self->priv->kerberos_context, credentials_cache); + if (identity == NULL) + { + g_simple_async_result_take_error (operation->result, error); + g_simple_async_result_set_op_res_gpointer (operation->result, + NULL, + NULL); + return; + } + } + else + { + g_object_ref (identity); + } + + g_hash_table_replace (self->priv->identities, + g_strdup (operation->identifier), + g_object_ref (identity)); + + if (!goa_kerberos_identity_sign_in (GOA_KERBEROS_IDENTITY (identity), + operation->identifier, + operation->initial_password, ++ operation->preauth_source, + operation->sign_in_flags, + (GoaIdentityInquiryFunc) + on_kerberos_identity_inquiry, + operation, + NULL, + operation->cancellable, + &error)) + { + g_simple_async_result_set_from_error (operation->result, error); + g_simple_async_result_set_op_res_gpointer (operation->result, + NULL, + NULL); + + } + else + { + g_simple_async_result_set_op_res_gpointer (operation->result, + g_object_ref (identity), + (GDestroyNotify) + g_object_unref); + } + + g_object_unref (identity); + } + + static void + sign_out_identity (GoaKerberosIdentityManager *self, + Operation *operation) + { + GError *error; +@@ -1156,83 +1162,85 @@ goa_kerberos_identity_manager_renew_identity (GoaIdentityManager *manager, + GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + goa_kerberos_identity_manager_renew_identity); + operation = operation_new (self, cancellable, OPERATION_TYPE_RENEW, result); + g_object_unref (result); + + operation->identity = g_object_ref (identity); + + g_async_queue_push (self->priv->pending_operations, operation); + } + + static void + goa_kerberos_identity_manager_renew_identity_finish (GoaIdentityManager *self, + GAsyncResult *result, + GError **error) + { + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), + error)) + return; + } + + static void + goa_kerberos_identity_manager_sign_identity_in (GoaIdentityManager *manager, + const char *identifier, + gconstpointer initial_password, ++ const char *preauth_source, + GoaIdentitySignInFlags flags, + GoaIdentityInquiryFunc inquiry_func, + gpointer inquiry_data, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) + { + GoaKerberosIdentityManager *self = GOA_KERBEROS_IDENTITY_MANAGER (manager); + GSimpleAsyncResult *result; + Operation *operation; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + goa_kerberos_identity_manager_sign_identity_in); + operation = operation_new (self, cancellable, OPERATION_TYPE_SIGN_IN, result); + g_object_unref (result); + + operation->identifier = g_strdup (identifier); + /* Not duped. Caller is responsible for ensuring it stays alive + * for duration of operation + */ + operation->initial_password = initial_password; ++ operation->preauth_source = g_strdup (preauth_source); + operation->sign_in_flags = flags; + operation->inquiry_func = inquiry_func; + operation->inquiry_data = inquiry_data; + g_mutex_init (&operation->inquiry_lock); + g_cond_init (&operation->inquiry_finished_condition); + operation->is_inquiring = FALSE; + + g_async_queue_push (self->priv->pending_operations, operation); + } + + static GoaIdentity * + goa_kerberos_identity_manager_sign_identity_in_finish (GoaIdentityManager *self, + GAsyncResult *result, + GError **error) + { + GoaIdentity *identity; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return NULL; + + identity = + g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)); + + return identity; + } + + static void + goa_kerberos_identity_manager_sign_identity_out (GoaIdentityManager *manager, + GoaIdentity *identity, + GCancellable *cancellable, +-- +1.8.3.1 + diff --git a/SOURCES/leak-crash-fixes.patch b/SOURCES/leak-crash-fixes.patch new file mode 100644 index 0000000..059fa14 --- /dev/null +++ b/SOURCES/leak-crash-fixes.patch @@ -0,0 +1,3030 @@ +From 6a71ae5ca9254b1072faeffbc5ad943e6204a340 Mon Sep 17 00:00:00 2001 +From: Ray Strode +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 +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 +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 +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 +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 +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 +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 +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 +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 + */ + + #include "config.h" + #include + + #include "goalogging.h" + #include "goaprovider.h" + #include "goaprovider-priv.h" + #include "goakerberosprovider.h" + #include "goaeditablelabel.h" + #include "goautils.h" + #include "goaidentity.h" + + #include + + #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 +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 + */ + + #include "config.h" + #include + #include + #include + #include + + #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 . ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include ++ ++#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 + diff --git a/SOURCES/whitelist-for-providers.patch b/SOURCES/whitelist-for-providers.patch new file mode 100644 index 0000000..b688bd2 --- /dev/null +++ b/SOURCES/whitelist-for-providers.patch @@ -0,0 +1,665 @@ +From c162d008c08d507d9007e5ec8b30fe89c6e47357 Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Mon, 19 May 2014 18:32:19 +0200 +Subject: [PATCH 1/5] provider: Use g_type_ensure for registering types + +Fixes: https://bugzilla.gnome.org/729173 +--- + src/goabackend/goaprovider.c | 22 +++++++++------------- + 1 file changed, 9 insertions(+), 13 deletions(-) + +diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c +index 4fa30cf..89c3ff0 100644 +--- a/src/goabackend/goaprovider.c ++++ b/src/goabackend/goaprovider.c +@@ -1,6 +1,6 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* +- * Copyright (C) 2011, 2012, 2013 Red Hat, Inc. ++ * Copyright (C) 2011, 2012, 2013, 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 +@@ -653,39 +653,35 @@ ensure_builtins_loaded (void) + + if (g_once_init_enter (&once_init_value)) + { +- static volatile GType type = 0; +- + /* The order is in which the providers' types are created is + * important because it affects the order in which they are + * returned by goa_provider_get_all. + */ + #ifdef GOA_GOOGLE_ENABLED +- type = GOA_TYPE_GOOGLE_PROVIDER; ++ g_type_ensure (GOA_TYPE_GOOGLE_PROVIDER); + #endif + #ifdef GOA_OWNCLOUD_ENABLED +- type = GOA_TYPE_OWNCLOUD_PROVIDER; ++ g_type_ensure (GOA_TYPE_OWNCLOUD_PROVIDER); + #endif + #ifdef GOA_FACEBOOK_ENABLED +- type = GOA_TYPE_FACEBOOK_PROVIDER; ++ g_type_ensure (GOA_TYPE_FACEBOOK_PROVIDER); + #endif + #ifdef GOA_FLICKR_ENABLED +- type = GOA_TYPE_FLICKR_PROVIDER; ++ g_type_ensure (GOA_TYPE_FLICKR_PROVIDER); + #endif + #ifdef GOA_WINDOWS_LIVE_ENABLED +- type = GOA_TYPE_WINDOWS_LIVE_PROVIDER; ++ g_type_ensure (GOA_TYPE_WINDOWS_LIVE_PROVIDER); + #endif + #ifdef GOA_EXCHANGE_ENABLED +- type = GOA_TYPE_EXCHANGE_PROVIDER; ++ g_type_ensure (GOA_TYPE_EXCHANGE_PROVIDER); + #endif + #ifdef GOA_IMAP_SMTP_ENABLED +- type = GOA_TYPE_IMAP_SMTP_PROVIDER; ++ g_type_ensure (GOA_TYPE_IMAP_SMTP_PROVIDER); + #endif + #ifdef GOA_KERBEROS_ENABLED +- type = GOA_TYPE_KERBEROS_PROVIDER; ++ g_type_ensure (GOA_TYPE_KERBEROS_PROVIDER); + #endif + +- type = type; /* silence -Wunused-but-set-variable */ +- + g_once_init_leave (&once_init_value, 1); + } + } +-- +1.9.3 + + +From 727bd59cbd0f36124b33d199d511c21a300af91a Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Thu, 29 May 2014 16:02:13 +0200 +Subject: [PATCH 2/5] Consolidate the provider types and extension point names + +It makes obvious sense to use the name of the extension point as the +provider type. So lets define a single macro instead of replicating the +string literal all over the code. + +This will also be useful when we implement the whitelist because the +same strings can be used in the GSettings key. + +Fixes: https://bugzilla.gnome.org/729173 +--- + configure.ac | 8 ++++++++ + src/goabackend/goaexchangeprovider.c | 4 ++-- + src/goabackend/goafacebookprovider.c | 4 ++-- + src/goabackend/goaflickrprovider.c | 4 ++-- + src/goabackend/goagoogleprovider.c | 4 ++-- + src/goabackend/goaimapsmtpprovider.c | 4 ++-- + src/goabackend/goakerberosprovider.c | 4 ++-- + src/goabackend/goaowncloudprovider.c | 4 ++-- + src/goabackend/goawindowsliveprovider.c | 4 ++-- + 9 files changed, 24 insertions(+), 16 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 7fc0cca..db0259e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -107,6 +107,7 @@ GOBJECT_INTROSPECTION_CHECK([0.6.2]) + # + + # Microsoft Exchange ++AC_DEFINE(GOA_EXCHANGE_NAME, ["exchange"], [ProviderType and extension point name]) + AC_ARG_ENABLE([exchange], + [AS_HELP_STRING([--enable-exchange], + [Enable Microsoft Exchange provider])], +@@ -117,6 +118,7 @@ if test "$enable_exchange" != "no"; then + fi + + # Flickr ++AC_DEFINE(GOA_FLICKR_NAME, ["flickr"], [ProviderType and extension point name]) + AC_ARG_ENABLE([flickr], [AS_HELP_STRING([--enable-flickr], [Enable Flickr provider])], [], [enable_flickr=no]) + AC_ARG_WITH(flickr-consumer-key, + [AS_HELP_STRING([--with-flickr-consumer-key], +@@ -141,6 +143,7 @@ if test "$enable_flickr" != "no"; then + fi + + # Google ++AC_DEFINE(GOA_GOOGLE_NAME, ["google"], [ProviderType and extension point name]) + AC_ARG_ENABLE([google], [AS_HELP_STRING([--enable-google], [Enable Google provider])], [], [enable_google=yes]) + AC_ARG_WITH(google-client-id, + [AS_HELP_STRING([--with-google-client-id], +@@ -169,6 +172,7 @@ if test "$enable_google" != "no"; then + fi + + # IMAP/SMTP ++AC_DEFINE(GOA_IMAP_SMTP_NAME, ["imap_smtp"], [ProviderType and extension point name]) + AC_ARG_ENABLE([imap-smtp], + [AS_HELP_STRING([--enable-imap-smtp], + [Enable IMAP/SMTP provider])], +@@ -179,6 +183,7 @@ if test "$enable_imap_smtp" != "no"; then + fi + + # ownCloud ++AC_DEFINE(GOA_OWNCLOUD_NAME, ["owncloud"], [ProviderType and extension point name]) + AC_ARG_ENABLE([owncloud], + [AS_HELP_STRING([--enable-owncloud], + [Enable ownCloud provider])], +@@ -189,6 +194,7 @@ if test "$enable_owncloud" != "no"; then + fi + + # Facebook ++AC_DEFINE(GOA_FACEBOOK_NAME, ["facebook"], [ProviderType and extension point name]) + AC_ARG_ENABLE([facebook], [AS_HELP_STRING([--enable-facebook], + [Enable Facebook provider])], + [], +@@ -210,6 +216,7 @@ if test "$enable_facebook" != "no"; then + fi + + # Windows Live ++AC_DEFINE(GOA_WINDOWS_LIVE_NAME, ["windows_live"], [ProviderType and extension point name]) + AC_ARG_ENABLE([windows-live], [AS_HELP_STRING([--enable-windows-live], + [Enable Windows Live provider])], + [], +@@ -230,6 +237,7 @@ if test "$enable_windows_live" != "no"; then + fi + + # Kerberos ++AC_DEFINE(GOA_KERBEROS_NAME, ["kerberos"], [ProviderType and extension point name]) + AC_ARG_ENABLE([kerberos], + [AS_HELP_STRING([--enable-kerberos], + [Enable kerberos support])], +diff --git a/src/goabackend/goaexchangeprovider.c b/src/goabackend/goaexchangeprovider.c +index 7b72d38..1244d70 100644 +--- a/src/goabackend/goaexchangeprovider.c ++++ b/src/goabackend/goaexchangeprovider.c +@@ -62,7 +62,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaExchangeProvider, goa_exchange_provider, GOA_TYPE_PR + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "exchange", ++ GOA_EXCHANGE_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -70,7 +70,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaExchangeProvider, goa_exchange_provider, GOA_TYPE_PR + static const gchar * + get_provider_type (GoaProvider *provider) + { +- return "exchange"; ++ return GOA_EXCHANGE_NAME; + } + + static gchar * +diff --git a/src/goabackend/goafacebookprovider.c b/src/goabackend/goafacebookprovider.c +index 21da0e7..37b519a 100644 +--- a/src/goabackend/goafacebookprovider.c ++++ b/src/goabackend/goafacebookprovider.c +@@ -64,7 +64,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaFacebookProvider, goa_facebook_provider, GOA_TYPE_OA + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "facebook", ++ GOA_FACEBOOK_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -72,7 +72,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaFacebookProvider, goa_facebook_provider, GOA_TYPE_OA + static const gchar * + get_provider_type (GoaProvider *_provider) + { +- return "facebook"; ++ return GOA_FACEBOOK_NAME; + } + + static gchar * +diff --git a/src/goabackend/goaflickrprovider.c b/src/goabackend/goaflickrprovider.c +index a472669..c6204a4 100644 +--- a/src/goabackend/goaflickrprovider.c ++++ b/src/goabackend/goaflickrprovider.c +@@ -65,7 +65,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaFlickrProvider, goa_flickr_provider, GOA_TYPE_OAUTH_ + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "flickr", ++ GOA_FLICKR_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -73,7 +73,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaFlickrProvider, goa_flickr_provider, GOA_TYPE_OAUTH_ + static const gchar * + get_provider_type (GoaProvider *_provider) + { +- return "flickr"; ++ return GOA_FLICKR_NAME; + } + + static gchar * +diff --git a/src/goabackend/goagoogleprovider.c b/src/goabackend/goagoogleprovider.c +index 065845d..6538023 100644 +--- a/src/goabackend/goagoogleprovider.c ++++ b/src/goabackend/goagoogleprovider.c +@@ -64,7 +64,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaGoogleProvider, goa_google_provider, GOA_TYPE_OAUTH2 + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "google", ++ GOA_GOOGLE_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -72,7 +72,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaGoogleProvider, goa_google_provider, GOA_TYPE_OAUTH2 + static const gchar * + get_provider_type (GoaProvider *_provider) + { +- return "google"; ++ return GOA_GOOGLE_NAME; + } + + static gchar * +diff --git a/src/goabackend/goaimapsmtpprovider.c b/src/goabackend/goaimapsmtpprovider.c +index 841327d..77972f8 100644 +--- a/src/goabackend/goaimapsmtpprovider.c ++++ b/src/goabackend/goaimapsmtpprovider.c +@@ -65,7 +65,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaImapSmtpProvider, goa_imap_smtp_provider, GOA_TYPE_P + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "imap_smtp", ++ GOA_IMAP_SMTP_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -73,7 +73,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaImapSmtpProvider, goa_imap_smtp_provider, GOA_TYPE_P + static const gchar * + get_provider_type (GoaProvider *provider) + { +- return "imap_smtp"; ++ return GOA_IMAP_SMTP_NAME; + } + + static gchar * +diff --git a/src/goabackend/goakerberosprovider.c b/src/goabackend/goakerberosprovider.c +index fbe0364..7fe77f6 100644 +--- a/src/goabackend/goakerberosprovider.c ++++ b/src/goabackend/goakerberosprovider.c +@@ -72,13 +72,13 @@ G_DEFINE_TYPE_WITH_CODE (GoaKerberosProvider, goa_kerberos_provider, GOA_TYPE_PR + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "kerberos", ++ GOA_KERBEROS_NAME, + 0)); + + static const gchar * + get_provider_type (GoaProvider *provider) + { +- return "kerberos"; ++ return GOA_KERBEROS_NAME; + } + + static gchar * +diff --git a/src/goabackend/goaowncloudprovider.c b/src/goabackend/goaowncloudprovider.c +index b154271..cbc86d8 100644 +--- a/src/goabackend/goaowncloudprovider.c ++++ b/src/goabackend/goaowncloudprovider.c +@@ -66,7 +66,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaOwncloudProvider, goa_owncloud_provider, GOA_TYPE_PR + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "owncloud", ++ GOA_OWNCLOUD_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -78,7 +78,7 @@ static const gchar *WEBDAV_ENDPOINT = "remote.php/webdav/"; + static const gchar * + get_provider_type (GoaProvider *provider) + { +- return "owncloud"; ++ return GOA_OWNCLOUD_NAME; + } + + static gchar * +diff --git a/src/goabackend/goawindowsliveprovider.c b/src/goabackend/goawindowsliveprovider.c +index 3d6abf9..bb71a75 100644 +--- a/src/goabackend/goawindowsliveprovider.c ++++ b/src/goabackend/goawindowsliveprovider.c +@@ -66,7 +66,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaWindowsLiveProvider, goa_windows_live_provider, GOA_ + goa_provider_ensure_extension_points_registered (); + g_io_extension_point_implement (GOA_PROVIDER_EXTENSION_POINT_NAME, + g_define_type_id, +- "windows_live", ++ GOA_WINDOWS_LIVE_NAME, + 0)); + + /* ---------------------------------------------------------------------------------------------------- */ +@@ -74,7 +74,7 @@ G_DEFINE_TYPE_WITH_CODE (GoaWindowsLiveProvider, goa_windows_live_provider, GOA_ + static const gchar * + get_provider_type (GoaProvider *_provider) + { +- return "windows_live"; ++ return GOA_WINDOWS_LIVE_NAME; + } + + static gchar * +-- +1.9.3 + + +From 84b57857c22d72790db751853aecfacc527f9eca Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Wed, 4 Jun 2014 10:57:11 +0200 +Subject: [PATCH 3/5] build: Remove unused variable + +Fixes: https://bugzilla.gnome.org/729173 +--- + configure.ac | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index db0259e..6de8b52 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -322,7 +322,7 @@ fi + # Internationalization + # + +-IT_PROG_INTLTOOL([$INTLTOOL_REQUIRED]) ++IT_PROG_INTLTOOL + GETTEXT_PACKAGE=gnome-online-accounts + AC_SUBST([GETTEXT_PACKAGE]) + AM_GLIB_GNU_GETTEXT +-- +1.9.3 + + +From 97e597ab73f4b2f5c692d3823941f89731aa6d98 Mon Sep 17 00:00:00 2001 +From: Philip Withnall +Date: Fri, 2 May 2014 15:44:31 +0200 +Subject: [PATCH 4/5] build: Remove deprecated call to GNOME_COMMON_INIT + +It just sets ACLOCAL_AMFLAGS, which is already correctly set in +Makefile.am. + +Also remove a duplicate AC_CONFIG_MACRO_DIR call. + +https://bugzilla.gnome.org/show_bug.cgi?id=729389 +--- + configure.ac | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/configure.ac b/configure.ac +index 6de8b52..6fb3525 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -48,7 +48,6 @@ AC_DEFINE(GOA_MICRO_VERSION, + # Initialization + # + +-GNOME_COMMON_INIT + GNOME_DEBUG_CHECK + GNOME_COMPILE_WARNINGS([maximum]) + GNOME_MAINTAINER_MODE_DEFINES +-- +1.9.3 + + +From 2d9f667964bf75396acd7f694e74d0637e90f7ed Mon Sep 17 00:00:00 2001 +From: Debarshi Ray +Date: Thu, 29 May 2014 14:21:26 +0200 +Subject: [PATCH 5/5] Add a whitelist for providers + +The whitelist is a GSetting where you can either list the names of the +providers that should be enabled or use 'all' to have everything. + +Bump minimum intltool version to 0.50.1 for GSettings XML support. + +Modelled off gnome-settings-daemon whitelist. + +Fixes: https://bugzilla.gnome.org/729173 +--- + configure.ac | 4 +- + data/Makefile.am | 8 +++ + data/org.gnome.online-accounts.gschema.xml.in | 11 +++ + po/POTFILES.in | 1 + + src/goabackend/goaprovider.c | 96 +++++++++++++++++++++------ + src/goabackend/goautils.h | 5 +- + 6 files changed, 103 insertions(+), 22 deletions(-) + create mode 100644 data/org.gnome.online-accounts.gschema.xml.in + +diff --git a/configure.ac b/configure.ac +index 6fb3525..eb00a98 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -48,6 +48,7 @@ AC_DEFINE(GOA_MICRO_VERSION, + # Initialization + # + ++GLIB_GSETTINGS + GNOME_DEBUG_CHECK + GNOME_COMPILE_WARNINGS([maximum]) + GNOME_MAINTAINER_MODE_DEFINES +@@ -321,7 +322,7 @@ fi + # Internationalization + # + +-IT_PROG_INTLTOOL ++IT_PROG_INTLTOOL([0.50.1]) + GETTEXT_PACKAGE=gnome-online-accounts + AC_SUBST([GETTEXT_PACKAGE]) + AM_GLIB_GNU_GETTEXT +@@ -381,6 +382,7 @@ data/icons/24x24/Makefile + data/icons/32x32/Makefile + data/icons/48x48/Makefile + data/icons/256x256/Makefile ++data/org.gnome.online-accounts.gschema.xml + src/Makefile + src/goa/Makefile + src/goa/goa-1.0.pc +diff --git a/data/Makefile.am b/data/Makefile.am +index 49004b8..cb30eb8 100644 +--- a/data/Makefile.am ++++ b/data/Makefile.am +@@ -6,6 +6,12 @@ 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 + service_DATA = $(service_in_files:.service.in=.service) +@@ -15,11 +21,13 @@ $(service_DATA): $(service_in_files) Makefile + + EXTRA_DIST = \ + $(css_DATA) \ ++ $(gsettings_in_files) \ + $(service_in_files) \ + dbus-interfaces.xml \ + $(NULL) + + DISTCLEANFILES = \ ++ $(gsettings_SCHEMAS) \ + org.gnome.OnlineAccounts.service \ + $(NULL) + +diff --git a/data/org.gnome.online-accounts.gschema.xml.in b/data/org.gnome.online-accounts.gschema.xml.in +new file mode 100644 +index 0000000..e42845a +--- /dev/null ++++ b/data/org.gnome.online-accounts.gschema.xml.in +@@ -0,0 +1,11 @@ ++ ++ ++ ++ ['all'] ++ List of providers that are allowed to be loaded ++ ++ A list of strings representing the providers that are allowed to be loaded (default: 'all'). This is only evaluated on startup. ++ ++ ++ ++ +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 8a8279e..6435ed4 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -1,6 +1,7 @@ + [encoding: UTF-8] + # List of source files containing translatable strings. + # Please keep this file sorted alphabetically. ++data/org.gnome.online-accounts.gschema.xml.in + src/daemon/goadaemon.c + src/goabackend/goaewsclient.c + src/goabackend/goaexchangeprovider.c +diff --git a/src/goabackend/goaprovider.c b/src/goabackend/goaprovider.c +index 89c3ff0..e40a9bb 100644 +--- a/src/goabackend/goaprovider.c ++++ b/src/goabackend/goaprovider.c +@@ -644,44 +644,100 @@ goa_provider_ensure_extension_points_registered (void) + } + } + +-static void +-ensure_builtins_loaded (void) +-{ +- static gsize once_init_value = 0; +- +- goa_provider_ensure_extension_points_registered (); ++/* ---------------------------------------------------------------------------------------------------- */ + +- if (g_once_init_enter (&once_init_value)) +- { +- /* The order is in which the providers' types are created is +- * important because it affects the order in which they are +- * returned by goa_provider_get_all. +- */ ++static struct ++{ ++ const gchar *name; ++ GType (*get_type) (void); ++} ordered_builtins_map[] = { ++ /* The order is in which the providers' types are created is ++ * important because it affects the order in which they are ++ * returned by goa_provider_get_all. ++ */ + #ifdef GOA_GOOGLE_ENABLED +- g_type_ensure (GOA_TYPE_GOOGLE_PROVIDER); ++ { GOA_GOOGLE_NAME, goa_google_provider_get_type }, + #endif + #ifdef GOA_OWNCLOUD_ENABLED +- g_type_ensure (GOA_TYPE_OWNCLOUD_PROVIDER); ++ { GOA_OWNCLOUD_NAME, goa_owncloud_provider_get_type }, + #endif + #ifdef GOA_FACEBOOK_ENABLED +- g_type_ensure (GOA_TYPE_FACEBOOK_PROVIDER); ++ { GOA_FACEBOOK_NAME, goa_facebook_provider_get_type }, + #endif + #ifdef GOA_FLICKR_ENABLED +- g_type_ensure (GOA_TYPE_FLICKR_PROVIDER); ++ { GOA_FLICKR_NAME, goa_flickr_provider_get_type }, + #endif + #ifdef GOA_WINDOWS_LIVE_ENABLED +- g_type_ensure (GOA_TYPE_WINDOWS_LIVE_PROVIDER); ++ { GOA_WINDOWS_LIVE_NAME, goa_windows_live_provider_get_type }, + #endif + #ifdef GOA_EXCHANGE_ENABLED +- g_type_ensure (GOA_TYPE_EXCHANGE_PROVIDER); ++ { GOA_EXCHANGE_NAME, goa_exchange_provider_get_type }, + #endif + #ifdef GOA_IMAP_SMTP_ENABLED +- g_type_ensure (GOA_TYPE_IMAP_SMTP_PROVIDER); ++ { GOA_IMAP_SMTP_NAME, goa_imap_smtp_provider_get_type }, + #endif + #ifdef GOA_KERBEROS_ENABLED +- g_type_ensure (GOA_TYPE_KERBEROS_PROVIDER); ++ { GOA_KERBEROS_NAME, goa_kerberos_provider_get_type }, + #endif ++ { NULL, NULL } ++}; ++ ++static void ++ensure_builtins_loaded (void) ++{ ++ static gsize once_init_value = 0; ++ ++ goa_provider_ensure_extension_points_registered (); + ++ if (g_once_init_enter (&once_init_value)) ++ { ++ GSettings *settings; ++ gchar **whitelisted_providers; ++ guint i; ++ guint j; ++ gboolean all = FALSE; ++ ++ settings = g_settings_new (GOA_SETTINGS_SCHEMA); ++ whitelisted_providers = g_settings_get_strv (settings, GOA_SETTINGS_WHITELISTED_PROVIDERS); ++ ++ /* Enable everything if there is 'all'. */ ++ for (i = 0; whitelisted_providers[i] != NULL; i++) ++ { ++ if (g_strcmp0 (whitelisted_providers[i], "all") == 0) ++ { ++ g_debug ("Loading all providers: "); ++ for (j = 0; ordered_builtins_map[j].name != NULL; j++) ++ { ++ g_debug (" - %s", ordered_builtins_map[j].name); ++ g_type_ensure ((*ordered_builtins_map[j].get_type) ()); ++ } ++ ++ all = TRUE; ++ break; ++ } ++ } ++ ++ if (all) ++ goto cleanup; ++ ++ /* Otherwise try them one by one. */ ++ g_debug ("Loading whitelisted providers: "); ++ for (i = 0; ordered_builtins_map[i].name != NULL; i++) ++ { ++ for (j = 0; whitelisted_providers[j] != NULL; j++) ++ { ++ if (g_strcmp0 (whitelisted_providers[j], ordered_builtins_map[i].name) == 0) ++ { ++ g_debug (" - %s", ordered_builtins_map[j].name); ++ g_type_ensure ((*ordered_builtins_map[i].get_type) ()); ++ break; ++ } ++ } ++ } ++ ++ cleanup: ++ g_strfreev (whitelisted_providers); ++ g_object_unref (settings); + g_once_init_leave (&once_init_value, 1); + } + } +diff --git a/src/goabackend/goautils.h b/src/goabackend/goautils.h +index ab593e0..dec2363 100644 +--- a/src/goabackend/goautils.h ++++ b/src/goabackend/goautils.h +@@ -1,6 +1,6 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + /* +- * Copyright (C) 2012, 2013 Red Hat, Inc. ++ * Copyright (C) 2012, 2013, 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 +@@ -36,6 +36,9 @@ G_BEGIN_DECLS + + #define GOA_OAUTH2_ACCESS_DENIED "access_denied" + ++#define GOA_SETTINGS_SCHEMA "org.gnome.online-accounts" ++#define GOA_SETTINGS_WHITELISTED_PROVIDERS "whitelisted-providers" ++ + typedef gpointer (*GoaPeekInterfaceFunc) (GoaObject *); + + gboolean goa_utils_check_duplicate (GoaClient *client, +-- +1.9.3 + diff --git a/SPECS/gnome-online-accounts.spec b/SPECS/gnome-online-accounts.spec index 01fb218..ae54970 100644 --- a/SPECS/gnome-online-accounts.spec +++ b/SPECS/gnome-online-accounts.spec @@ -1,6 +1,6 @@ Name: gnome-online-accounts Version: 3.8.5 -Release: 9%{?dist}.1 +Release: 14%{?dist} Summary: Provide online accounts information Group: System Environment/Libraries @@ -8,13 +8,17 @@ License: LGPLv2+ URL: https://live.gnome.org/GnomeOnlineAccounts Source0: http://download.gnome.org/sources/gnome-online-accounts/3.8/%{name}-%{version}.tar.xz +BuildRequires: autoconf +BuildRequires: automake BuildRequires: gcr-devel BuildRequires: glib2-devel >= 2.35 +BuildRequires: gnome-common BuildRequires: gtk3-devel >= 3.5.1 BuildRequires: gobject-introspection-devel BuildRequires: gtk-doc BuildRequires: intltool BuildRequires: krb5-devel +BuildRequires: libtool BuildRequires: webkitgtk3-devel BuildRequires: json-glib-devel BuildRequires: libsecret-devel >= 0.7 @@ -27,6 +31,9 @@ Patch0: kerberos-fixes.patch Patch1: translations.patch Patch2: google-drop-password-based.patch Patch3: facebook-updates-and-fixes.patch +Patch4: whitelist-for-providers.patch +Patch5: add-smartcard-support.patch +Patch6: leak-crash-fixes.patch %description gnome-online-accounts provides interfaces so applications and @@ -49,8 +56,12 @@ files for developing applications that use gnome-online-accounts. %patch1 -p2 -b .translations %patch2 -p1 -b .google-drop-password-based %patch3 -p1 -b .facebook-updates-and-fixes +%patch4 -p1 -b .whitelist-for-providers +%patch5 -p1 -b .add-smartcard-support +%patch6 -p1 -b .leak-crash-fixes %build +autoreconf -fiv %configure \ --disable-static \ --enable-gtk-doc \ @@ -76,11 +87,13 @@ touch --no-create %{_datadir}/icons/hicolor &>/dev/null || : %postun /sbin/ldconfig if [ $1 -eq 0 ] ; then + /usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : touch --no-create %{_datadir}/icons/hicolor &>/dev/null gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : fi %posttrans +/usr/bin/glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || : gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %files -f %{name}.lang @@ -90,10 +103,13 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_libdir}/libgoa-1.0.so.0.0.0 %{_libdir}/libgoa-backend-1.0.so.0 %{_libdir}/libgoa-backend-1.0.so.0.0.0 -%{_prefix}/libexec/goa-daemon +%{_libexecdir}/goa-daemon +%{_libexecdir}/goa-identity-service %{_datadir}/dbus-1/services/org.gnome.OnlineAccounts.service +%{_datadir}/dbus-1/services/org.gnome.Identity.service %{_datadir}/icons/hicolor/*/apps/goa-*.png %{_datadir}/man/man8/goa-daemon.8.gz +%{_datadir}/glib-2.0/schemas/org.gnome.online-accounts.gschema.xml %{_datadir}/%{name}/goawebview.css %files devel @@ -109,17 +125,33 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : %{_libdir}/goa-1.0/include %changelog -* Fri May 23 2014 Ray Strode 3.8.5-9.1 -- Bump for dist tag confusion - Related: #1096399 +* Fri Oct 31 2014 Ray Strode 3.8.5-14 +- Fix provider object leak, sporadic crash, and a few other + leaks I found along the way. +Related: #1099115 -* Fri May 09 2014 Ray Strode 3.8.5-8.1 +* Wed Oct 22 2014 Ray Strode 3.8.5-13 +- Fix free() on uninitialized variable introduced in previous commit +Related: #864917 + +* Tue Oct 21 2014 Ray Strode - 3.8.5-12 +- Add initial smartcard support +Resolves: #864917 + +* Mon Aug 04 2014 Debarshi Ray - 3.8.5-11 +- Offer a way for admins to selectively disable providers +Resolves: #1080539 + +* Fri May 23 2014 Ray Strode 3.8.5-10 +- bump because of dist confusion + +* Fri May 09 2014 Ray Strode 3.8.5-9 - Fix various resource leaks in the kerberos code - Resolves: #1096399 +Resolves: #1029316 * Mon Mar 31 2014 Debarshi Ray - 3.8.5-8 - Popup is too small to display Facebook authorization - Resolves: #1081520 +Resolves: #1081520 * Fri Feb 28 2014 Matthias Clasen - 3.8.5-7 - Turn off silent builds and make them verbose @@ -130,26 +162,26 @@ Resolves: #1070809 * Tue Jan 07 2014 Debarshi Ray - 3.8.5-5 - Don't offer a PasswordBased interface for Google -- Resolves: #1049337 +Resolves: #1049337 * Fri Dec 27 2013 Daniel Mach - 3.8.5-4 - Mass rebuild 2013-12-27 * Wed Dec 11 2013 Matthias Clasen 3.8.5-3 - Update translations -- Resolves: #1030343 +Resolves: #1030343 * Wed Nov 06 2013 Ray Strode 3.8.5-2 - Fix kerberos crash when user manually erases stored credentials with seahorse - Resolves: #1027413 +Resolves: #1027413 * Wed Nov 06 2013 Debarshi Ray - 3.8.5-1 - Update to 3.8.5 -- Resolves: #1023117, #1027258 +Resolves: #1023117, #1027258 * Fri Nov 01 2013 Debarshi Ray - 3.8.4.1-2 - Support libkrb5's new kernel keyring based credentials cache -- Resolves: #991184 +Resolves: #991184 * Tue Oct 08 2013 Debarshi Ray - 3.8.4.1-1 - Update to 3.8.4.1