summaryrefslogtreecommitdiff
path: root/wocky/wocky-jabber-auth.c
diff options
context:
space:
mode:
authorEitan Isaacson <eitan.isaacson@collabora.co.uk>2010-04-30 15:37:43 -0700
committerEitan Isaacson <eitan.isaacson@collabora.co.uk>2010-05-07 13:47:49 -0700
commit2b0b9262008ffdb6b0b1db44441cd4a27c17aca6 (patch)
tree13c3fde80c317036b8739bf61ec501ab02235b03 /wocky/wocky-jabber-auth.c
parent68af7efd6de961ab2512d4960c5c2a343ea12e8b (diff)
Added WockyJabberAuth, WockyJabberAuthPassword and WockyJabberAuthDigest.
Diffstat (limited to 'wocky/wocky-jabber-auth.c')
-rw-r--r--wocky/wocky-jabber-auth.c672
1 files changed, 672 insertions, 0 deletions
diff --git a/wocky/wocky-jabber-auth.c b/wocky/wocky-jabber-auth.c
new file mode 100644
index 0000000..b7a1004
--- /dev/null
+++ b/wocky/wocky-jabber-auth.c
@@ -0,0 +1,672 @@
+/*
+ * wocky-jabber-auth.c - Source for WockyJabberAuth
+ * Copyright (C) 2009-2010 Collabora Ltd.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#include "wocky-jabber-auth.h"
+#include "wocky-signals-marshal.h"
+#include "wocky-namespaces.h"
+#include "wocky-utils.h"
+
+#define DEBUG_FLAG DEBUG_SASL
+#include "wocky-debug.h"
+
+G_DEFINE_TYPE(WockyJabberAuth, wocky_jabber_auth, G_TYPE_OBJECT)
+
+enum
+{
+ PROP_SESSION_ID = 1,
+ PROP_USERNAME,
+ PROP_RESOURCE,
+ PROP_PASSWORD,
+ PROP_CONNECTION,
+ PROP_AUTH_REGISTRY,
+};
+
+/* private structure */
+struct _WockyJabberAuthPrivate
+{
+ gboolean dispose_has_run;
+ WockyXmppConnection *connection;
+ gchar *username;
+ gchar *resource;
+ gchar *password;
+ gchar *session_id;
+ GCancellable *cancel;
+ GSimpleAsyncResult *result;
+ WockyAuthRegistry *auth_registry;
+ gboolean allow_plain;
+};
+
+static void
+wocky_jabber_auth_init (WockyJabberAuth *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, WOCKY_TYPE_JABBER_AUTH,
+ WockyJabberAuthPrivate);
+}
+
+static void wocky_jabber_auth_dispose (GObject *object);
+static void wocky_jabber_auth_finalize (GObject *object);
+
+static void
+wocky_jabber_auth_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ WockyJabberAuth *self = WOCKY_JABBER_AUTH (object);
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_SESSION_ID:
+ g_free (priv->session_id);
+ priv->session_id = g_value_dup_string (value);
+ break;
+ case PROP_USERNAME:
+ g_free (priv->username);
+ priv->username = g_value_dup_string (value);
+ break;
+ case PROP_RESOURCE:
+ g_free (priv->resource);
+ priv->resource = g_value_dup_string (value);
+ break;
+ case PROP_PASSWORD:
+ g_free (priv->password);
+ priv->password = g_value_dup_string (value);
+ break;
+ case PROP_CONNECTION:
+ priv->connection = g_value_dup_object (value);
+ break;
+ case PROP_AUTH_REGISTRY:
+ if (g_value_get_object (value) == NULL)
+ priv->auth_registry = wocky_auth_registry_new ();
+ else
+ priv->auth_registry = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+wocky_jabber_auth_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ WockyJabberAuth *self = WOCKY_JABBER_AUTH (object);
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ switch (property_id)
+ {
+ case PROP_SESSION_ID:
+ g_value_set_string (value, priv->session_id);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->connection);
+ break;
+ case PROP_AUTH_REGISTRY:
+ g_value_set_object (value, priv->auth_registry);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ break;
+ }
+}
+
+static void
+wocky_jabber_auth_class_init (WockyJabberAuthClass *wocky_jabber_auth_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (wocky_jabber_auth_class);
+ GParamSpec *spec;
+
+ g_type_class_add_private (wocky_jabber_auth_class,
+ sizeof (WockyJabberAuthPrivate));
+
+ object_class->set_property = wocky_jabber_auth_set_property;
+ object_class->get_property = wocky_jabber_auth_get_property;
+
+ spec = g_param_spec_string ("session-id",
+ "session-id",
+ "The XMPP session ID",
+ NULL,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_SESSION_ID, spec);
+
+ spec = g_param_spec_string ("username",
+ "username",
+ "The username to authenticate with",
+ NULL,
+ G_PARAM_WRITABLE|G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_USERNAME, spec);
+
+ spec = g_param_spec_string ("resource",
+ "resource",
+ "The XMPP resource to bind to",
+ NULL,
+ G_PARAM_WRITABLE|G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_RESOURCE, spec);
+
+ spec = g_param_spec_string ("password",
+ "password",
+ "The password to authenticate with",
+ NULL,
+ G_PARAM_WRITABLE|G_PARAM_CONSTRUCT);
+ g_object_class_install_property (object_class, PROP_PASSWORD, spec);
+
+ spec = g_param_spec_object ("connection",
+ "connection",
+ "The Xmpp connection to user",
+ WOCKY_TYPE_XMPP_CONNECTION,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_CONNECTION, spec);
+
+ spec = g_param_spec_object ("auth-registry",
+ "Authentication Registry",
+ "Authentication Registry",
+ WOCKY_TYPE_AUTH_REGISTRY,
+ G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_AUTH_REGISTRY, spec);
+
+ object_class->dispose = wocky_jabber_auth_dispose;
+ object_class->finalize = wocky_jabber_auth_finalize;
+
+}
+
+void
+wocky_jabber_auth_dispose (GObject *object)
+{
+ WockyJabberAuth *self = WOCKY_JABBER_AUTH (object);
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ if (priv->connection != NULL)
+ g_object_unref (priv->connection);
+
+ if (priv->auth_registry != NULL)
+ g_object_unref (priv->auth_registry);
+
+ if (G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->dispose)
+ G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->dispose (object);
+}
+
+void
+wocky_jabber_auth_finalize (GObject *object)
+{
+ WockyJabberAuth *self = WOCKY_JABBER_AUTH (object);
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ /* free any data held directly by the object here */
+ g_free (priv->session_id);
+ g_free (priv->username);
+ g_free (priv->resource);
+ g_free (priv->password);
+
+ G_OBJECT_CLASS (wocky_jabber_auth_parent_class)->finalize (object);
+}
+
+static void
+auth_reset (WockyJabberAuth *self)
+{
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ g_free (priv->session_id);
+ priv->session_id = NULL;
+
+ if (priv->connection != NULL)
+ {
+ g_object_unref (priv->connection);
+ priv->connection = NULL;
+ }
+}
+
+static void
+auth_succeeded (WockyJabberAuth *self)
+{
+ WockyJabberAuthPrivate *priv = self->priv;
+ GSimpleAsyncResult *r;
+
+ DEBUG ("Authentication succeeded");
+ auth_reset (self);
+
+ r = priv->result;
+ priv->result = NULL;
+
+ g_simple_async_result_complete (r);
+ g_object_unref (r);
+}
+
+static void
+auth_failed (WockyJabberAuth *self, gint error, const gchar *format, ...)
+{
+ gchar *message;
+ va_list args;
+ GSimpleAsyncResult *r;
+ WockyJabberAuthPrivate *priv = self->priv;
+
+ auth_reset (self);
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ DEBUG ("Authentication failed!: %s", message);
+
+ r = priv->result;
+ priv->result = NULL;
+
+ g_simple_async_result_set_error (r,
+ WOCKY_AUTH_ERROR, error, "%s", message);
+
+ g_simple_async_result_complete (r);
+ g_object_unref (r);
+
+ g_free (message);
+}
+
+static gboolean
+stream_error (WockyJabberAuth *self, WockyStanza *stanza)
+{
+ WockyStanzaType type = WOCKY_STANZA_TYPE_NONE;
+ WockyNode *xmpp = NULL;
+ GSList *item = NULL;
+ const gchar *msg = NULL;
+ const gchar *err = NULL;
+ WockyNode *cond = NULL;
+ WockyNode *text = NULL;
+
+ if (stanza == NULL)
+ {
+ auth_failed (self, WOCKY_AUTH_ERROR_CONNRESET, "Disconnected");
+ return TRUE;
+ }
+
+ wocky_stanza_get_type_info (stanza, &type, NULL);
+
+ if (type == WOCKY_STANZA_TYPE_STREAM_ERROR)
+ {
+ xmpp = wocky_stanza_get_top_node (stanza);
+ for (item = xmpp->children; item != NULL; item = g_slist_next (item))
+ {
+ WockyNode *child = item->data;
+ const gchar *cns = wocky_node_get_ns (child);
+
+ if (wocky_strdiff (cns, WOCKY_XMPP_NS_STREAMS))
+ continue;
+
+ if (!wocky_strdiff (child->name, "text"))
+ text = child;
+ else
+ cond = child;
+ }
+
+ if (text != NULL)
+ msg = text->content;
+ else if (cond != NULL)
+ msg = cond->name;
+ else
+ msg = "-";
+
+ err = (cond != NULL) ? cond->name : "unknown-error";
+
+ auth_failed (self, WOCKY_AUTH_ERROR_STREAM, "%s: %s", err, msg);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+WockyJabberAuth *
+wocky_jabber_auth_new (const gchar *session_id,
+ const gchar *username,
+ const gchar *resource,
+ const gchar *password,
+ WockyXmppConnection *connection,
+ WockyAuthRegistry *auth_registry)
+{
+ return g_object_new (WOCKY_TYPE_JABBER_AUTH,
+ "session-id", session_id,
+ "username", username,
+ "resource", resource,
+ "password", password,
+ "connection", connection,
+ "auth-registry", auth_registry,
+ NULL);
+}
+
+gboolean
+wocky_jabber_auth_authenticate_finish (WockyJabberAuth *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ wocky_implement_finish_void (self, wocky_jabber_auth_authenticate_finish);
+}
+
+static void
+wocky_jabber_auth_success_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ GError *error = NULL;
+
+ if (!wocky_auth_registry_success_finish (priv->auth_registry, res, &error))
+ {
+ auth_failed (self, error->code, error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ auth_succeeded (self);
+ }
+}
+
+static void
+jabber_auth_reply (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ GError *error = NULL;
+ WockyStanza *reply = NULL;
+ WockyStanzaType type = WOCKY_STANZA_TYPE_NONE;
+ WockyStanzaSubType sub = WOCKY_STANZA_SUB_TYPE_NONE;
+
+ DEBUG ("");
+ reply = wocky_xmpp_connection_recv_stanza_finish (conn, res, &error);
+
+ if (stream_error (self, reply))
+ return;
+
+ wocky_stanza_get_type_info (reply, &type, &sub);
+
+ if (type != WOCKY_STANZA_TYPE_IQ)
+ {
+ auth_failed (self, WOCKY_AUTH_ERROR_INVALID_REPLY,
+ "Jabber Auth Reply: Response Invalid");
+ goto out;
+ }
+
+ switch (sub)
+ {
+ WockyAuthError code;
+
+ case WOCKY_STANZA_SUB_TYPE_ERROR:
+ wocky_stanza_extract_errors (reply, NULL, &error, NULL, NULL);
+
+ switch (error->code)
+ {
+ case WOCKY_XMPP_ERROR_NOT_AUTHORIZED:
+ code = WOCKY_AUTH_ERROR_NOT_AUTHORIZED;
+ break;
+ case WOCKY_XMPP_ERROR_CONFLICT:
+ code = WOCKY_AUTH_ERROR_RESOURCE_CONFLICT;
+ break;
+ case WOCKY_XMPP_ERROR_NOT_ACCEPTABLE:
+ code = WOCKY_AUTH_ERROR_NO_CREDENTIALS;
+ break;
+ default:
+ code = WOCKY_AUTH_ERROR_FAILURE;
+ }
+
+ auth_failed (self, code, "Authentication failed: %s", error->message);
+ g_clear_error (&error);
+ break;
+
+ case WOCKY_STANZA_SUB_TYPE_RESULT:
+ wocky_auth_registry_success_async (priv->auth_registry,
+ wocky_jabber_auth_success_cb, self);
+ break;
+
+ default:
+ auth_failed (self, WOCKY_AUTH_ERROR_INVALID_REPLY,
+ "Bizarre response to Jabber Auth request");
+ break;
+ }
+
+ out:
+ g_object_unref (reply);
+
+}
+
+static void
+jabber_auth_query (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ GError *error = NULL;
+
+ DEBUG ("");
+ if (!wocky_xmpp_connection_send_stanza_finish (conn, res, &error))
+ {
+ auth_failed (self, error->code, "Jabber Auth IQ Set: %s",
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ wocky_xmpp_connection_recv_stanza_async (conn, NULL,
+ jabber_auth_reply, user_data);
+}
+
+static void
+wocky_jabber_auth_start_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ gchar *iqid;
+ WockyStanza *iq;
+ GString *initial_response = NULL;
+ gchar *mechanism = NULL;
+ const gchar *auth_field;
+ GError *error = NULL;
+
+ iqid = wocky_xmpp_connection_new_id (conn);
+ if (!wocky_auth_registry_start_auth_finish (priv->auth_registry, res,
+ &mechanism, &initial_response, &error))
+ {
+ auth_failed (self, error->code, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_assert (initial_response != NULL);
+ g_assert (mechanism != NULL);
+
+ if (g_strcmp0 (mechanism, "X-WOCKY-JABBER-PASSWORD") == 0)
+ auth_field = "password";
+ else
+ auth_field = "digest";
+
+ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ,
+ WOCKY_STANZA_SUB_TYPE_SET, NULL, NULL,
+ '@', "id", iqid,
+ '(', "query", ':', WOCKY_JABBER_NS_AUTH,
+ '(', "username", '$', priv->username, ')',
+ '(', auth_field, '$', initial_response->str, ')',
+ '(', "resource", '$', priv->resource, ')',
+ ')',
+ NULL);
+
+ wocky_xmpp_connection_send_stanza_async (conn, iq, NULL,
+ jabber_auth_query, self);
+
+ g_free (iqid);
+ g_free (mechanism);
+ g_object_unref (iq);
+ g_string_free (initial_response, TRUE);
+}
+
+static void
+jabber_auth_fields (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ GError *error = NULL;
+ WockyStanza *fields = NULL;
+ WockyStanzaType type = WOCKY_STANZA_TYPE_NONE;
+ WockyStanzaSubType sub = WOCKY_STANZA_SUB_TYPE_NONE;
+
+ fields = wocky_xmpp_connection_recv_stanza_finish (conn, res, &error);
+
+ if (stream_error (self, fields))
+ return;
+
+ wocky_stanza_get_type_info (fields, &type, &sub);
+
+ if (type != WOCKY_STANZA_TYPE_IQ)
+ {
+ auth_failed (self, WOCKY_AUTH_ERROR_FAILURE,
+ "Jabber Auth Init: Response Invalid");
+ goto out;
+ }
+
+ switch (sub)
+ {
+ WockyNode *node = NULL;
+ WockyAuthError code;
+ gboolean passwd;
+ gboolean digest;
+
+ case WOCKY_STANZA_SUB_TYPE_ERROR:
+ wocky_stanza_extract_errors (fields, NULL, &error, NULL, NULL);
+
+ if (error->code == WOCKY_XMPP_ERROR_SERVICE_UNAVAILABLE)
+ code = WOCKY_AUTH_ERROR_NOT_SUPPORTED;
+ else
+ code = WOCKY_AUTH_ERROR_FAILURE;
+
+ auth_failed (self, code, "Jabber Auth: %s %s",
+ wocky_xmpp_error_string (error->code),
+ error->message);
+ g_clear_error (&error);
+ break;
+
+ case WOCKY_STANZA_SUB_TYPE_RESULT:
+ passwd = FALSE;
+ digest = FALSE;
+ node = wocky_stanza_get_top_node (fields);
+ node = wocky_node_get_child_ns (node, "query",
+ WOCKY_JABBER_NS_AUTH);
+ if ((node != NULL) &&
+ (wocky_node_get_child (node, "resource") != NULL) &&
+ (wocky_node_get_child (node, "username") != NULL))
+ {
+ GSList *mechanisms = NULL;
+
+ if (wocky_node_get_child (node, "password") != NULL)
+ mechanisms = g_slist_append (mechanisms,
+ "X-WOCKY-JABBER-PASSWORD");
+
+ if (wocky_node_get_child (node, "digest") != NULL)
+ mechanisms = g_slist_append (mechanisms,
+ "X-WOCKY-JABBER-DIGEST");
+
+ wocky_auth_registry_start_auth_async (priv->auth_registry,
+ mechanisms, priv->allow_plain, FALSE,
+ priv->username, priv->password, NULL, priv->session_id,
+ wocky_jabber_auth_start_cb, self);
+
+ g_slist_free (mechanisms);
+ }
+ break;
+
+ default:
+ auth_failed (self, WOCKY_AUTH_ERROR_FAILURE,
+ "Bizarre response to Jabber Auth request");
+ break;
+ }
+
+ out:
+ g_object_unref (fields);
+}
+
+static void
+jabber_auth_init_sent (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ WockyJabberAuth *self = (WockyJabberAuth *) user_data;
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ GError *error = NULL;
+
+ DEBUG ("");
+ if (!wocky_xmpp_connection_send_stanza_finish (conn, res, &error))
+ {
+ auth_failed (self, error->code, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ wocky_xmpp_connection_recv_stanza_async (conn, NULL,
+ jabber_auth_fields, user_data);
+}
+
+/* Initiate jabber auth. features should contain the stream features stanza as
+ * receiver from the session_id */
+void
+wocky_jabber_auth_authenticate_async (WockyJabberAuth *self,
+ gboolean allow_plain,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WockyJabberAuthPrivate *priv = self->priv;
+ WockyXmppConnection *conn = priv->connection;
+ gchar *id = wocky_xmpp_connection_new_id (conn);
+ WockyStanza *iq = NULL;
+
+ DEBUG ("");
+
+ priv->allow_plain = allow_plain;
+ priv->result = g_simple_async_result_new (G_OBJECT (self),
+ callback, user_data, wocky_jabber_auth_authenticate_finish);
+
+ iq = wocky_stanza_build (WOCKY_STANZA_TYPE_IQ, WOCKY_STANZA_SUB_TYPE_GET,
+ NULL, NULL,
+ '@', "id", id,
+ '(', "query", ':', WOCKY_JABBER_NS_AUTH,
+ '(', "username",
+ '$', priv->username,
+ ')',
+ ')',
+ NULL);
+
+ wocky_xmpp_connection_send_stanza_async (conn, iq, NULL,
+ jabber_auth_init_sent, self);
+
+ g_free (id);
+ g_object_unref (iq);
+}