2 * empathy-auth-factory.c - Source for EmpathyAuthFactory
3 * Copyright (C) 2010 Collabora Ltd.
4 * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "empathy-auth-factory.h"
25 #include <telepathy-glib/telepathy-glib.h>
27 #define DEBUG_FLAG EMPATHY_DEBUG_TLS
28 #include "empathy-debug.h"
29 #include "empathy-keyring.h"
30 #include "empathy-server-sasl-handler.h"
31 #include "empathy-server-tls-handler.h"
32 #include "empathy-utils.h"
35 #include "empathy-goa-auth-handler.h"
38 #include "extensions/extensions.h"
40 G_DEFINE_TYPE (EmpathyAuthFactory, empathy_auth_factory, TP_TYPE_BASE_CLIENT);
42 struct _EmpathyAuthFactoryPriv {
43 /* Keep a ref here so the auth client doesn't have to mess with
44 * refs. It will be cleared when the channel (and so the handler)
47 * The channel path of the handler's channel (borrowed gchar *) ->
48 * reffed (EmpathyServerSASLHandler *)
50 GHashTable *sasl_handlers;
53 EmpathyGoaAuthHandler *goa_handler;
60 NEW_SERVER_TLS_HANDLER,
61 NEW_SERVER_SASL_HANDLER,
65 static guint signals[LAST_SIGNAL] = { 0, };
67 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyAuthFactory)
69 static EmpathyAuthFactory *auth_factory_singleton = NULL;
72 TpHandleChannelsContext *context;
73 EmpathyAuthFactory *self;
77 handler_context_data_free (HandlerContextData *data)
79 tp_clear_object (&data->self);
80 tp_clear_object (&data->context);
82 g_slice_free (HandlerContextData, data);
85 static HandlerContextData *
86 handler_context_data_new (EmpathyAuthFactory *self,
87 TpHandleChannelsContext *context)
89 HandlerContextData *data;
91 data = g_slice_new0 (HandlerContextData);
92 data->self = g_object_ref (self);
95 data->context = g_object_ref (context);
101 server_tls_handler_ready_cb (GObject *source,
105 EmpathyServerTLSHandler *handler;
106 GError *error = NULL;
107 HandlerContextData *data = user_data;
109 handler = empathy_server_tls_handler_new_finish (res, &error);
113 DEBUG ("Failed to create a server TLS handler; error %s",
115 tp_handle_channels_context_fail (data->context, error);
117 g_error_free (error);
121 tp_handle_channels_context_accept (data->context);
122 g_signal_emit (data->self, signals[NEW_SERVER_TLS_HANDLER], 0,
125 g_object_unref (handler);
128 handler_context_data_free (data);
132 sasl_handler_invalidated_cb (EmpathyServerSASLHandler *handler,
135 EmpathyAuthFactory *self = user_data;
136 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
139 channel = empathy_server_sasl_handler_get_channel (handler);
140 g_assert (channel != NULL);
142 DEBUG ("SASL handler for channel %s is invalidated, unref it",
143 tp_proxy_get_object_path (channel));
145 g_hash_table_remove (priv->sasl_handlers, tp_proxy_get_object_path (channel));
149 server_sasl_handler_ready_cb (GObject *source,
153 EmpathyAuthFactoryPriv *priv;
154 GError *error = NULL;
155 HandlerContextData *data = user_data;
156 EmpathyServerSASLHandler *handler;
158 priv = GET_PRIV (data->self);
159 handler = empathy_server_sasl_handler_new_finish (res, &error);
163 DEBUG ("Failed to create a server SASL handler; error %s",
166 if (data->context != NULL)
167 tp_handle_channels_context_fail (data->context, error);
169 g_error_free (error);
175 if (data->context != NULL)
176 tp_handle_channels_context_accept (data->context);
178 channel = empathy_server_sasl_handler_get_channel (handler);
179 g_assert (channel != NULL);
181 /* Pass the ref to the hash table */
182 g_hash_table_insert (priv->sasl_handlers,
183 (gpointer) tp_proxy_get_object_path (channel), handler);
185 tp_g_signal_connect_object (handler, "invalidated",
186 G_CALLBACK (sasl_handler_invalidated_cb), data->self, 0);
188 g_signal_emit (data->self, signals[NEW_SERVER_SASL_HANDLER], 0,
192 handler_context_data_free (data);
196 common_checks (EmpathyAuthFactory *self,
201 EmpathyAuthFactoryPriv *priv = GET_PRIV (self);
203 const GError *dbus_error;
204 EmpathyServerSASLHandler *handler;
206 /* there can't be more than one ServerTLSConnection or
207 * ServerAuthentication channels at the same time, for the same
208 * connection/account.
210 if (g_list_length (channels) != 1)
212 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
213 "Can't %s more than one ServerTLSConnection or ServerAuthentication "
214 "channel for the same connection.", observe ? "observe" : "handle");
219 channel = channels->data;
221 if (tp_channel_get_channel_type_id (channel) !=
222 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
224 /* If we are observing we care only about ServerAuthentication channels.
225 * If we are handling we care about ServerAuthentication and
226 * ServerTLSConnection channels. */
228 || tp_channel_get_channel_type_id (channel) !=
229 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
231 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
232 "Can only %s ServerTLSConnection or ServerAuthentication channels, "
233 "this was a %s channel", observe ? "observe" : "handle",
234 tp_channel_get_channel_type (channel));
240 handler = g_hash_table_lookup (priv->sasl_handlers,
241 tp_proxy_get_object_path (channel));
243 if (tp_channel_get_channel_type_id (channel) ==
244 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION
245 && handler != NULL &&
248 g_set_error (error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
249 "We are already handling this channel: %s",
250 tp_proxy_get_object_path (channel));
255 dbus_error = tp_proxy_get_invalidated (channel);
256 if (dbus_error != NULL)
258 *error = g_error_copy (dbus_error);
266 handle_channels (TpBaseClient *handler,
268 TpConnection *connection,
270 GList *requests_satisfied,
271 gint64 user_action_time,
272 TpHandleChannelsContext *context)
275 GError *error = NULL;
276 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (handler);
277 HandlerContextData *data;
279 DEBUG ("Handle TLS or SASL carrier channels.");
281 if (!common_checks (self, channels, FALSE, &error))
283 DEBUG ("Failed checks: %s", error->message);
284 tp_handle_channels_context_fail (context, error);
285 g_clear_error (&error);
289 /* The common checks above have checked this is fine. */
290 channel = channels->data;
292 /* Only password authentication is supported from here */
293 if (tp_channel_get_channel_type_id (channel) ==
294 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION &&
295 !empathy_sasl_channel_supports_mechanism (channel,
296 "X-TELEPATHY-PASSWORD"))
298 g_set_error_literal (&error, TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
299 "Only the X-TELEPATHY-PASSWORD SASL mechanism is supported");
300 DEBUG ("%s", error->message);
301 tp_handle_channels_context_fail (context, error);
302 g_clear_error (&error);
306 data = handler_context_data_new (self, context);
307 tp_handle_channels_context_delay (context);
309 /* create a handler */
310 if (tp_channel_get_channel_type_id (channel) ==
311 EMP_IFACE_QUARK_CHANNEL_TYPE_SERVER_TLS_CONNECTION)
313 empathy_server_tls_handler_new_async (channel, server_tls_handler_ready_cb,
316 else if (tp_channel_get_channel_type_id (channel) ==
317 TP_IFACE_QUARK_CHANNEL_TYPE_SERVER_AUTHENTICATION)
319 empathy_server_sasl_handler_new_async (account, channel,
320 server_sasl_handler_ready_cb, data);
326 EmpathyAuthFactory *self;
327 TpObserveChannelsContext *context;
328 TpChannelDispatchOperation *dispatch_operation;
331 } ObserveChannelsData;
334 observe_channels_data_free (ObserveChannelsData *data)
336 g_object_unref (data->context);
337 g_object_unref (data->account);
338 g_object_unref (data->channel);
339 g_object_unref (data->dispatch_operation);
340 g_slice_free (ObserveChannelsData, data);
344 password_claim_cb (GObject *source,
345 GAsyncResult *result,
348 ObserveChannelsData *data = user_data;
349 GError *error = NULL;
351 if (!tp_channel_dispatch_operation_claim_with_finish (
352 TP_CHANNEL_DISPATCH_OPERATION (source), result, &error))
354 DEBUG ("Failed to call Claim: %s", error->message);
355 g_clear_error (&error);
359 HandlerContextData *h_data;
361 DEBUG ("Claim called successfully");
363 h_data = handler_context_data_new (data->self, NULL);
365 empathy_server_sasl_handler_new_async (TP_ACCOUNT (data->account),
366 data->channel, server_sasl_handler_ready_cb, h_data);
369 observe_channels_data_free (data);
373 get_password_cb (GObject *source,
374 GAsyncResult *result,
377 ObserveChannelsData *data = user_data;
379 if (empathy_keyring_get_account_password_finish (TP_ACCOUNT (source), result, NULL) == NULL)
381 /* We don't actually mind if this fails, just let the approver
382 * go ahead and take the channel. */
384 DEBUG ("We don't have a password for account %s, letting the event "
385 "manager approver take it", tp_proxy_get_object_path (source));
387 tp_observe_channels_context_accept (data->context);
388 observe_channels_data_free (data);
392 DEBUG ("We have a password for account %s, calling Claim",
393 tp_proxy_get_object_path (source));
395 tp_channel_dispatch_operation_claim_with_async (data->dispatch_operation,
396 TP_BASE_CLIENT (data->self), password_claim_cb, data);
398 tp_observe_channels_context_accept (data->context);
404 goa_claim_cb (GObject *source,
405 GAsyncResult *result,
408 ObserveChannelsData *data = user_data;
409 EmpathyAuthFactory *self = data->self;
410 GError *error = NULL;
412 if (!tp_channel_dispatch_operation_claim_with_finish (data->dispatch_operation,
415 DEBUG ("Failed to claim: %s", error->message);
416 g_clear_error (&error);
420 empathy_goa_auth_handler_start (self->priv->goa_handler,
421 data->channel, data->account);
424 observe_channels_data_free (data);
426 #endif /* HAVE_GOA */
429 observe_channels (TpBaseClient *client,
431 TpConnection *connection,
433 TpChannelDispatchOperation *dispatch_operation,
435 TpObserveChannelsContext *context)
437 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (client);
439 GError *error = NULL;
440 ObserveChannelsData *data;
442 DEBUG ("New auth channel to observe");
444 if (!common_checks (self, channels, TRUE, &error))
446 DEBUG ("Failed checks: %s", error->message);
447 tp_observe_channels_context_fail (context, error);
448 g_clear_error (&error);
452 /* The common checks above have checked this is fine. */
453 channel = channels->data;
455 data = g_slice_new0 (ObserveChannelsData);
457 data->context = g_object_ref (context);
458 data->dispatch_operation = g_object_ref (dispatch_operation);
459 data->account = g_object_ref (account);
460 data->channel = g_object_ref (channel);
464 if (empathy_goa_auth_handler_supports (self->priv->goa_handler, channel, account))
466 DEBUG ("Supported GOA account (%s), claim SASL channel",
467 tp_proxy_get_object_path (account));
469 tp_channel_dispatch_operation_claim_with_async (dispatch_operation,
470 client, goa_claim_cb, data);
471 tp_observe_channels_context_accept (context);
474 #endif /* HAVE_GOA */
477 if (empathy_sasl_channel_supports_mechanism (data->channel,
478 "X-TELEPATHY-PASSWORD"))
480 empathy_keyring_get_account_password_async (data->account,
481 get_password_cb, data);
482 tp_observe_channels_context_delay (context);
487 error = g_error_new_literal (TP_ERROR, TP_ERROR_INVALID_ARGUMENT,
488 "Unknown auth mechanism");
489 tp_observe_channels_context_fail (context, error);
490 g_clear_error (&error);
492 observe_channels_data_free (data);
496 empathy_auth_factory_constructor (GType type,
498 GObjectConstructParam *params)
502 if (auth_factory_singleton != NULL)
504 retval = g_object_ref (auth_factory_singleton);
508 retval = G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructor
509 (type, n_params, params);
511 auth_factory_singleton = EMPATHY_AUTH_FACTORY (retval);
512 g_object_add_weak_pointer (retval, (gpointer *) &auth_factory_singleton);
519 empathy_auth_factory_init (EmpathyAuthFactory *self)
521 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
522 EMPATHY_TYPE_AUTH_FACTORY, EmpathyAuthFactoryPriv);
524 self->priv->sasl_handlers = g_hash_table_new_full (g_str_hash, g_str_equal,
525 NULL, g_object_unref);
527 self->priv->goa_handler = empathy_goa_auth_handler_new ();
528 #endif /* HAVE_GOA */
532 empathy_auth_factory_constructed (GObject *obj)
534 EmpathyAuthFactory *self = EMPATHY_AUTH_FACTORY (obj);
535 TpBaseClient *client = TP_BASE_CLIENT (self);
537 /* chain up to TpBaseClient first */
538 G_OBJECT_CLASS (empathy_auth_factory_parent_class)->constructed (obj);
540 tp_base_client_set_handler_bypass_approval (client, FALSE);
542 /* Handle ServerTLSConnection and ServerAuthentication channels */
543 tp_base_client_take_handler_filter (client, tp_asv_new (
545 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
546 EMP_IFACE_CHANNEL_TYPE_SERVER_TLS_CONNECTION,
547 /* AuthenticationMethod */
548 TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT,
549 TP_HANDLE_TYPE_NONE, NULL));
551 tp_base_client_take_handler_filter (client, tp_asv_new (
553 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
554 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
555 /* AuthenticationMethod */
556 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
557 G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
560 /* We are also an observer so that we can see new auth channels
561 * popping up and if we have the password already saved to one
562 * account where an auth channel has just appeared we can call
563 * Claim() on the CDO so the approver won't get it, which makes
566 /* Observe ServerAuthentication channels */
567 tp_base_client_take_observer_filter (client, tp_asv_new (
569 TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING,
570 TP_IFACE_CHANNEL_TYPE_SERVER_AUTHENTICATION,
571 /* AuthenticationMethod */
572 TP_PROP_CHANNEL_TYPE_SERVER_AUTHENTICATION_AUTHENTICATION_METHOD,
573 G_TYPE_STRING, TP_IFACE_CHANNEL_INTERFACE_SASL_AUTHENTICATION,
576 tp_base_client_set_observer_delay_approvers (client, TRUE);
580 empathy_auth_factory_dispose (GObject *object)
582 EmpathyAuthFactoryPriv *priv = GET_PRIV (object);
584 if (priv->dispose_run)
587 priv->dispose_run = TRUE;
589 g_hash_table_unref (priv->sasl_handlers);
591 g_object_unref (priv->goa_handler);
592 #endif /* HAVE_GOA */
594 G_OBJECT_CLASS (empathy_auth_factory_parent_class)->dispose (object);
598 empathy_auth_factory_class_init (EmpathyAuthFactoryClass *klass)
600 GObjectClass *oclass = G_OBJECT_CLASS (klass);
601 TpBaseClientClass *base_client_cls = TP_BASE_CLIENT_CLASS (klass);
603 oclass->constructor = empathy_auth_factory_constructor;
604 oclass->constructed = empathy_auth_factory_constructed;
605 oclass->dispose = empathy_auth_factory_dispose;
607 base_client_cls->handle_channels = handle_channels;
608 base_client_cls->observe_channels = observe_channels;
610 g_type_class_add_private (klass, sizeof (EmpathyAuthFactoryPriv));
612 signals[NEW_SERVER_TLS_HANDLER] =
613 g_signal_new ("new-server-tls-handler",
614 G_TYPE_FROM_CLASS (klass),
615 G_SIGNAL_RUN_LAST, 0,
617 g_cclosure_marshal_generic,
619 1, EMPATHY_TYPE_SERVER_TLS_HANDLER);
621 signals[NEW_SERVER_SASL_HANDLER] =
622 g_signal_new ("new-server-sasl-handler",
623 G_TYPE_FROM_CLASS (klass),
624 G_SIGNAL_RUN_LAST, 0,
626 g_cclosure_marshal_generic,
628 1, EMPATHY_TYPE_SERVER_SASL_HANDLER);
632 empathy_auth_factory_new (TpSimpleClientFactory *factory)
634 return g_object_new (EMPATHY_TYPE_AUTH_FACTORY,
636 "name", "Empathy.Auth",
641 empathy_auth_factory_register (EmpathyAuthFactory *self,
644 return tp_base_client_register (TP_BASE_CLIENT (self), error);