diff --git a/src/empathy-auth-client.c b/src/empathy-auth-client.c index 3d48aea..51cc213 100644 --- a/src/empathy-auth-client.c +++ b/src/empathy-auth-client.c @@ -1,4 +1,8 @@ /* + * Copyright (C) 2012 Jolla Ltd. + * Contact: John Brooks + * + * Based on Empathy, * Copyright (C) 2010 Collabora Ltd. * * This program is free software; you can redistribute it and/or @@ -29,23 +33,6 @@ #include #include #include -#if 0 -#include -#define DEBUG_FLAG EMPATHY_DEBUG_TLS -#include -#include - -#include -#include -#include -#include - -#include "empathy-sanity-cleaning.h" - -#include - -#include -#endif #define TIMEOUT 60 @@ -303,9 +290,9 @@ main (int argc, return EXIT_FAILURE; } - DEBUG ("Empathy auth client started."); + DEBUG ("SASL signon auth client started."); - if (g_getenv ("EMPATHY_PERSIST") != NULL) + if (g_getenv ("SASL_SIGNON_PERSIST") != NULL) { DEBUG ("Timed-exit disabled"); diff --git a/src/empathy-auth-factory.c b/src/empathy-auth-factory.c index 427f24b..30a4dc4 100644 --- a/src/empathy-auth-factory.c +++ b/src/empathy-auth-factory.c @@ -22,13 +22,11 @@ #include "empathy-debug.h" #include "empathy-utils.h" -//#include "empathy-keyring.h" -//#include "empathy-sasl-mechanisms.h" +#include "empathy-keyring.h" +#include "empathy-sasl-mechanisms.h" #include "empathy-server-sasl-handler.h" #include "empathy-server-tls-handler.h" -//#include "empathy-utils.h" -// XXX #ifdef HAVE_UOA #include "empathy-uoa-auth-handler.h" #endif /* HAVE_UOA */ @@ -648,6 +646,7 @@ empathy_auth_factory_constructed (GObject *obj) tp_base_client_set_handler_bypass_approval (client, FALSE); /* Handle ServerTLSConnection and ServerAuthentication channels */ + /* TLS channels are disabled for now due to redundant functionality. */ #if 0 tp_base_client_take_handler_filter (client, tp_asv_new ( /* ChannelType */ diff --git a/src/empathy-keyring.c b/src/empathy-keyring.c index b4ed906..d428daa 100644 --- a/src/empathy-keyring.c +++ b/src/empathy-keyring.c @@ -30,9 +30,6 @@ #include #include "empathy-uoa-utils.h" -#define EMPATHY_UOA_SERVICE_TYPE "IM" -#define EMPATHY_UOA_PROVIDER "im.telepathy.Account.Storage.UOA" - static AgAccountService * uoa_password_common (TpAccount *tp_account, GSimpleAsyncResult *result, diff --git a/src/empathy-uoa-auth-handler.c b/src/empathy-uoa-auth-handler.c new file mode 100644 index 0000000..26c9277 --- /dev/null +++ b/src/empathy-uoa-auth-handler.c @@ -0,0 +1,394 @@ +/* + * empathy-auth-uoa.c - Source for Uoa SASL authentication + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define DEBUG_FLAG EMPATHY_DEBUG_SASL +#include "empathy-debug.h" +#include "empathy-keyring.h" +#include "empathy-utils.h" +#include "empathy-uoa-auth-handler.h" +#include "empathy-uoa-utils.h" +#include "empathy-sasl-mechanisms.h" + +struct _EmpathyUoaAuthHandlerPriv +{ + AgManager *manager; +}; + +G_DEFINE_TYPE (EmpathyUoaAuthHandler, empathy_uoa_auth_handler, G_TYPE_OBJECT); + +static void +empathy_uoa_auth_handler_init (EmpathyUoaAuthHandler *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + EMPATHY_TYPE_UOA_AUTH_HANDLER, EmpathyUoaAuthHandlerPriv); + + self->priv->manager = empathy_uoa_manager_dup (); +} + +static void +empathy_uoa_auth_handler_dispose (GObject *object) +{ + EmpathyUoaAuthHandler *self = (EmpathyUoaAuthHandler *) object; + + tp_clear_object (&self->priv->manager); + + G_OBJECT_CLASS (empathy_uoa_auth_handler_parent_class)->dispose (object); +} + +static void +empathy_uoa_auth_handler_class_init (EmpathyUoaAuthHandlerClass *klass) +{ + GObjectClass *oclass = G_OBJECT_CLASS (klass); + + oclass->dispose = empathy_uoa_auth_handler_dispose; + + g_type_class_add_private (klass, sizeof (EmpathyUoaAuthHandlerPriv)); +} + +EmpathyUoaAuthHandler * +empathy_uoa_auth_handler_new (void) +{ + return g_object_new (EMPATHY_TYPE_UOA_AUTH_HANDLER, NULL); +} + +typedef struct +{ + TpChannel *channel; + AgAccountService *service; + AgAuthData *auth_data; + SignonIdentity *identity; + SignonAuthSession *session; + + gchar *username; +} AuthContext; + +static AuthContext * +auth_context_new (TpChannel *channel, + AgAccountService *service) +{ + AuthContext *ctx; + guint cred_id; + + ctx = g_slice_new0 (AuthContext); + ctx->channel = g_object_ref (channel); + ctx->service = g_object_ref (service); + + ctx->auth_data = ag_account_service_get_auth_data (service); + if (ctx->auth_data == NULL) + goto out; + + cred_id = ag_auth_data_get_credentials_id (ctx->auth_data); + if (cred_id == 0) + goto out; + + ctx->identity = signon_identity_new_from_db (cred_id); + if (ctx->identity == NULL) + goto out; + + ctx->session = signon_identity_create_session (ctx->identity, + ag_auth_data_get_method (ctx->auth_data), NULL); + if (ctx->session == NULL) + goto out; + +out: + return ctx; +} + +static void +auth_context_free (AuthContext *ctx) +{ + g_clear_object (&ctx->channel); + g_clear_object (&ctx->service); + tp_clear_pointer (&ctx->auth_data, ag_auth_data_unref); + g_clear_object (&ctx->session); + g_clear_object (&ctx->identity); + g_free (ctx->username); + + g_slice_free (AuthContext, ctx); +} + +static void +auth_context_done (AuthContext *ctx) +{ + tp_channel_close_async (ctx->channel, NULL, NULL); + auth_context_free (ctx); +} + +static void +request_password_session_process_cb (SignonAuthSession *session, + GHashTable *session_data, + const GError *error, + gpointer user_data) +{ + AuthContext *ctx = user_data; + + if (error != NULL) + { + DEBUG ("Error processing the session to request user's attention: %s", + error->message); + } + + auth_context_done (ctx); +} + +static void +request_password (AuthContext *ctx) +{ + GHashTable *extra_params; + + DEBUG ("Invalid credentials, request user action"); + + /* Inform SSO that the access token (or password) didn't work and it should + * ask user to re-grant access (or retype password). */ + extra_params = tp_asv_new ( + SIGNON_SESSION_DATA_UI_POLICY, G_TYPE_INT, + SIGNON_POLICY_REQUEST_PASSWORD, + NULL); + + ag_auth_data_insert_parameters (ctx->auth_data, extra_params); + + signon_auth_session_process (ctx->session, + ag_auth_data_get_parameters (ctx->auth_data), + ag_auth_data_get_mechanism (ctx->auth_data), + request_password_session_process_cb, ctx); + + g_hash_table_unref (extra_params); +} + +static void +auth_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpChannel *channel = (TpChannel *) source; + AuthContext *ctx = user_data; + GError *error = NULL; + + if (!empathy_sasl_auth_finish (channel, result, &error)) + { + DEBUG ("SASL Mechanism error: %s", error->message); + g_clear_error (&error); + + request_password (ctx); + } + else + { + DEBUG ("Auth on %s suceeded", tp_proxy_get_object_path (channel)); + auth_context_done (ctx); + } +} + +static void +session_process_cb (SignonAuthSession *session, + GHashTable *session_data, + const GError *error, + gpointer user_data) +{ + AuthContext *ctx = user_data; + const gchar *access_token; + const gchar *client_id; + + if (error != NULL) + { + DEBUG ("Error processing the session: %s", error->message); + auth_context_done (ctx); + return; + } + + access_token = tp_asv_get_string (session_data, "AccessToken"); + client_id = tp_asv_get_string (ag_auth_data_get_parameters (ctx->auth_data), + "ClientId"); + + switch (empathy_sasl_channel_select_mechanism (ctx->channel)) + { + case EMPATHY_SASL_MECHANISM_FACEBOOK: + empathy_sasl_auth_facebook_async (ctx->channel, + client_id, access_token, + auth_cb, ctx); + break; + + case EMPATHY_SASL_MECHANISM_WLM: + empathy_sasl_auth_wlm_async (ctx->channel, + access_token, + auth_cb, ctx); + break; + + case EMPATHY_SASL_MECHANISM_GOOGLE: + empathy_sasl_auth_google_async (ctx->channel, + ctx->username, access_token, + auth_cb, ctx); + break; + + case EMPATHY_SASL_MECHANISM_PASSWORD: + empathy_sasl_auth_password_async (ctx->channel, + tp_asv_get_string (session_data, "Secret"), + auth_cb, ctx); + break; + + default: + g_assert_not_reached (); + } +} + +static void +identity_query_info_cb (SignonIdentity *identity, + const SignonIdentityInfo *info, + const GError *error, + gpointer user_data) +{ + AuthContext *ctx = user_data; + + if (error != NULL) + { + DEBUG ("Error querying info from identity: %s", error->message); + auth_context_done (ctx); + return; + } + + ctx->username = g_strdup (signon_identity_info_get_username (info)); + + signon_auth_session_process (ctx->session, + ag_auth_data_get_parameters (ctx->auth_data), + ag_auth_data_get_mechanism (ctx->auth_data), + session_process_cb, + ctx); +} + +static void +set_account_password_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpAccount *tp_account = (TpAccount *) source; + AuthContext *ctx = user_data; + AuthContext *new_ctx; + GError *error = NULL; + + if (!empathy_keyring_set_account_password_finish (tp_account, result, &error)) + { + DEBUG ("Failed to set empty password on UOA account: %s", error->message); + auth_context_done (ctx); + return; + } + + new_ctx = auth_context_new (ctx->channel, ctx->service); + auth_context_free (ctx); + + if (new_ctx->session != NULL) + { + /* The trick worked! */ + request_password (new_ctx); + return; + } + + DEBUG ("Still can't get a signon session, even after setting empty pwd"); + auth_context_done (new_ctx); +} + +void +empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *tp_account) +{ + const GValue *id_value; + AgAccountId id; + AgAccount *account; + GList *l = NULL; + AgAccountService *service; + AuthContext *ctx; + + g_return_if_fail (TP_IS_CHANNEL (channel)); + g_return_if_fail (TP_IS_ACCOUNT (tp_account)); + g_return_if_fail (empathy_uoa_auth_handler_supports (self, channel, + tp_account)); + + DEBUG ("Start UOA auth for account: %s", + tp_proxy_get_object_path (tp_account)); + + id_value = tp_account_get_storage_identifier (tp_account); + id = g_value_get_uint (id_value); + + account = ag_manager_get_account (self->priv->manager, id); + if (account != NULL) + l = ag_account_list_services_by_type (account, EMPATHY_UOA_SERVICE_TYPE); + if (l == NULL) + { + DEBUG ("Couldn't find IM service for AgAccountId %u", id); + g_object_unref (account); + tp_channel_close_async (channel, NULL, NULL); + return; + } + + /* Assume there is only one IM service */ + service = ag_account_service_new (account, l->data); + ag_service_list_free (l); + g_object_unref (account); + + ctx = auth_context_new (channel, service); + if (ctx->session == NULL) + { + /* This (usually?) means we never stored credentials for this account. + * To ask user to type his password SSO needs a SignonIdentity bound to + * our account. Let's store an empty password. */ + DEBUG ("Couldn't create a signon session"); + empathy_keyring_set_account_password_async (tp_account, "", FALSE, + set_account_password_cb, ctx); + } + else + { + /* All is fine! Query UOA for more info */ + signon_identity_query_info (ctx->identity, + identity_query_info_cb, ctx); + } + + g_object_unref (service); +} + +gboolean +empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account) +{ + const gchar *provider; + EmpathySaslMechanism mech; + + g_return_val_if_fail (TP_IS_CHANNEL (channel), FALSE); + g_return_val_if_fail (TP_IS_ACCOUNT (account), FALSE); + + provider = tp_account_get_storage_provider (account); + + if (tp_strdiff (provider, EMPATHY_UOA_PROVIDER)) + return FALSE; + + mech = empathy_sasl_channel_select_mechanism (channel); + return mech == EMPATHY_SASL_MECHANISM_FACEBOOK || + mech == EMPATHY_SASL_MECHANISM_WLM || + mech == EMPATHY_SASL_MECHANISM_GOOGLE || + mech == EMPATHY_SASL_MECHANISM_PASSWORD; +} diff --git a/src/empathy-uoa-auth-handler.h b/src/empathy-uoa-auth-handler.h new file mode 100644 index 0000000..2c61a76 --- /dev/null +++ b/src/empathy-uoa-auth-handler.h @@ -0,0 +1,72 @@ +/* + * empathy-auth-uoa.h - Header for Uoa SASL authentication + * Copyright (C) 2012 Collabora Ltd. + * @author Xavier Claessens + * + * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __EMPATHY_UOA_AUTH_HANDLER_H__ +#define __EMPATHY_UOA_AUTH_HANDLER_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _EmpathyUoaAuthHandler EmpathyUoaAuthHandler; +typedef struct _EmpathyUoaAuthHandlerClass EmpathyUoaAuthHandlerClass; +typedef struct _EmpathyUoaAuthHandlerPriv EmpathyUoaAuthHandlerPriv; + +struct _EmpathyUoaAuthHandlerClass { + GObjectClass parent_class; +}; + +struct _EmpathyUoaAuthHandler { + GObject parent; + EmpathyUoaAuthHandlerPriv *priv; +}; + +GType empathy_uoa_auth_handler_get_type (void); + +/* TYPE MACROS */ +#define EMPATHY_TYPE_UOA_AUTH_HANDLER \ + (empathy_uoa_auth_handler_get_type ()) +#define EMPATHY_UOA_AUTH_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandler)) +#define EMPATHY_UOA_AUTH_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandlerClass)) +#define EMPATHY_IS_UOA_AUTH_HANDLER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER)) +#define EMPATHY_IS_UOA_AUTH_HANDLER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), EMPATHY_TYPE_UOA_AUTH_HANDLER)) +#define EMPATHY_UOA_AUTH_HANDLER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_UOA_AUTH_HANDLER, \ + EmpathyUoaAuthHandlerClass)) + +EmpathyUoaAuthHandler *empathy_uoa_auth_handler_new (void); + +void empathy_uoa_auth_handler_start (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account); + +gboolean empathy_uoa_auth_handler_supports (EmpathyUoaAuthHandler *self, + TpChannel *channel, + TpAccount *account); + +G_END_DECLS + +#endif /* #ifndef __EMPATHY_UOA_AUTH_HANDLER_H__*/ diff --git a/telepathy-sasl-signon.pro b/telepathy-sasl-signon.pro index 637d599..c9efc21 100644 --- a/telepathy-sasl-signon.pro +++ b/telepathy-sasl-signon.pro @@ -4,6 +4,8 @@ CONFIG += link_pkgconfig PKGCONFIG += libsignon-glib telepathy-glib libaccounts-glib libsoup-2.4 INCLUDEPATH += src +DEFINES += HAVE_UOA \ + EMPATHY_UOA_PROVIDER=\\\"im.telepathy.Account.Storage.UOA\\\" SOURCES += src/empathy-auth-client.c \ src/empathy-auth-factory.c \ @@ -11,7 +13,8 @@ SOURCES += src/empathy-auth-client.c \ src/empathy-server-tls-handler.c \ src/empathy-keyring.c \ src/empathy-uoa-utils.c \ - src/empathy-sasl-mechanisms.c + src/empathy-sasl-mechanisms.c \ + src/empathy-uoa-auth-handler.c HEADERS += src/empathy-auth-factory.h \ src/empathy-debug.h \ @@ -20,7 +23,8 @@ HEADERS += src/empathy-auth-factory.h \ src/empathy-server-sasl-handler.h \ src/empathy-server-tls-handler.h \ src/empathy-uoa-utils.h \ - src/empathy-utils.h + src/empathy-utils.h \ + src/empathy-uoa-auth-handler.h OTHER_FILES += org.freedesktop.Telepathy.Client.SaslSignonAuth.service \ SaslSignonAuth.client