--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/loudmouth/src/lm-sasl.c Tue Feb 02 01:10:06 2010 +0200
@@ -0,0 +1,876 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Collabora Ltd.
+ *
+ * This program 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 program 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 program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+
+//hack - to get over compilation error
+#include <glib_global.h>
+#include "lm-sock.h"
+#include "lm-debug.h"
+#include "lm-error.h"
+#include "lm-internals.h"
+#include "lm-message-queue.h"
+#include "lm-misc.h"
+#include "lm-ssl-internals.h"
+#include "lm-parser.h"
+#include "lm-sha.h"
+#include "lm-connection.h"
+#include "lm-utils.h"
+#include "lm-socket.h"
+#include "lm-sasl.h"
+
+#include "md5.h"
+#include "base64.h"
+
+typedef enum {
+ AUTH_TYPE_PLAIN = 1,
+ AUTH_TYPE_DIGEST = 2
+} AuthType;
+
+typedef enum {
+ SASL_AUTH_STATE_NO_MECH,
+ SASL_AUTH_STATE_PLAIN_STARTED,
+ SASL_AUTH_STATE_DIGEST_MD5_STARTED,
+ SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE,
+ SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE,
+} SaslAuthState;
+
+struct _LmSASL {
+ LmConnection *connection;
+ AuthType auth_type;
+ SaslAuthState state;
+ gchar *username;
+ gchar *password;
+ gchar *server;
+ gchar *digest_md5_rspauth;
+ LmMessageHandler *features_cb;
+ LmMessageHandler *challenge_cb;
+ LmMessageHandler *success_cb;
+ LmMessageHandler *failure_cb;
+
+ gboolean features_received;
+ gboolean start_auth;
+
+ LmSASLResultHandler handler;
+};
+
+#define XMPP_NS_SASL_AUTH "urn:ietf:params:xml:ns:xmpp-sasl"
+
+static LmHandlerResult sasl_features_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data);
+
+static LmHandlerResult sasl_challenge_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data);
+
+static LmHandlerResult sasl_success_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data);
+
+static LmHandlerResult sasl_failure_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data);
+
+
+/* DIGEST-MD5 mechanism code from libgibber */
+
+static gchar *
+sasl_strndup_unescaped (const gchar *str, gsize len)
+{
+ const gchar *s;
+ gchar *d;
+ gchar *ret;
+
+ ret = g_malloc0 (len + 1);
+ for (s = str, d = ret ; s < (str + len) ; s++, d++) {
+ if (*s == '\\') s++;
+ *d = *s;
+ }
+
+ return ret;
+}
+
+static GHashTable *
+sasl_digest_md5_challenge_to_hash (const gchar * challenge)
+{
+ const gchar *keystart, *keyend, *valstart;
+ const gchar *c = challenge;
+ gchar *key, *val;
+ GHashTable *result;
+
+ result = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+
+ do {
+ while (g_ascii_isspace(*c)) c++;
+
+ keystart = c;
+ for (; *c != '\0' && *c != '='; c++);
+
+ if (*c == '\0' || c == keystart) goto error;
+
+ keyend = c;
+ c++;
+
+ if (*c == '"') {
+ c++;
+ valstart = c;
+ for (; *c != '\0' && *c != '"'; c++);
+ if (*c == '\0' || c == valstart) goto error;
+ val = sasl_strndup_unescaped (valstart, c - valstart);
+ c++;
+ } else {
+ valstart = c;
+ for (; *c != '\0' && *c != ','; c++);
+ if (c == valstart) goto error;
+ val = g_strndup (valstart, c - valstart);
+ }
+
+ key = g_strndup (keystart, keyend - keystart);
+
+ g_hash_table_insert (result, key, val);
+
+ if (*c == ',') c++;
+ } while (*c != '\0');
+
+ return result;
+error:
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "Failed to parse challenge: %s", challenge);*/
+ lm_verbose("[sasl_digest_md5_challenge_to_hash]:Failed to parse challenge: %s", challenge);
+ g_hash_table_destroy (result);
+ return NULL;
+}
+
+static gchar *
+sasl_md5_hex_hash (gchar *value, gsize len)
+{
+ md5_byte_t digest_md5[16];
+ md5_state_t md5_calc;
+ GString *str;
+ int i;
+
+ str = g_string_sized_new (32);
+
+ md5_init (&md5_calc);
+ md5_append (&md5_calc, (const md5_byte_t *)value, len);
+ md5_finish (&md5_calc, digest_md5);
+
+ for (i = 0 ; i < 16 ; i++) {
+ g_string_append_printf (str, "%02x", digest_md5[i]);
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static gchar *
+sasl_digest_md5_generate_cnonce(void)
+{
+ /* RFC 2831 recommends the the nonce to be either hexadecimal or base64 with
+ * at least 64 bits of entropy */
+#define NR 8
+ guint32 n[NR];
+ int i;
+
+ for (i = 0; i < NR; i++) {
+ n[i] = g_random_int();
+ }
+
+ return base64_encode ((gchar *)n, sizeof(n));
+}
+
+static gchar *
+sasl_md5_prepare_response (LmSASL *sasl, GHashTable *challenge)
+{
+ GString *response;
+ const gchar *realm, *nonce;
+ gchar *a1, *a1h, *a2, *a2h, *kd, *kdh;
+ gchar *cnonce = NULL;
+ gchar *tmp;
+ md5_byte_t digest_md5[16];
+ md5_state_t md5_calc;
+ gsize len;
+
+ response = g_string_new ("");
+
+ if (sasl->username == NULL || sasl->password == NULL) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: no username or password provided", G_STRFUNC);*/
+ lm_verbose("[sasl_md5_prepare_response]: %s: no username or password provided", G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "no username/password provided");
+ }
+ goto error;
+ }
+
+ nonce = g_hash_table_lookup (challenge, "nonce");
+ if (nonce == NULL || nonce == '\0') {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: server didn't provide a nonce in the challenge",
+ G_STRFUNC);*/
+ lm_verbose("[sasl_md5_prepare_response]: %s: server didn't provide a nonce in the challenge", G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+ goto error;
+ }
+
+ cnonce = sasl_digest_md5_generate_cnonce ();
+
+ /* FIXME challenge can contain multiple realms */
+ realm = g_hash_table_lookup (challenge, "realm");
+ if (realm == NULL) {
+ realm = sasl->server;
+ }
+
+ /* FIXME properly escape values */
+ g_string_append_printf (response, "username=\"%s\"", sasl->username);
+ g_string_append_printf (response, ",realm=\"%s\"", realm);
+ g_string_append_printf (response, ",digest-uri=\"xmpp/%s\"", realm);
+ g_string_append_printf (response, ",nonce=\"%s\",nc=00000001", nonce);
+ g_string_append_printf (response, ",cnonce=\"%s\"", cnonce);
+ /* FIXME should check if auth is in the cop challenge val */
+ g_string_append_printf (response, ",qop=auth,charset=utf-8");
+
+ tmp = g_strdup_printf ("%s:%s:%s",
+ sasl->username, realm, sasl->password);
+ md5_init (&md5_calc);
+ md5_append (&md5_calc, (const md5_byte_t *)tmp, strlen(tmp));
+ md5_finish (&md5_calc, digest_md5);
+ g_free (tmp);
+
+ a1 = g_strdup_printf ("0123456789012345:%s:%s", nonce, cnonce);
+ len = strlen (a1);
+ memcpy (a1, digest_md5, 16);
+ a1h = sasl_md5_hex_hash (a1, len);
+
+ a2 = g_strdup_printf ("AUTHENTICATE:xmpp/%s", realm);
+ a2h = sasl_md5_hex_hash (a2, strlen(a2));
+
+ kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s",
+ a1h, nonce, cnonce, a2h);
+ kdh = sasl_md5_hex_hash (kd, strlen(kd));
+ g_string_append_printf (response, ",response=%s", kdh);
+
+ g_free (kd);
+ g_free (kdh);
+ g_free (a2);
+ g_free (a2h);
+
+ /* Calculate the response we expect from the server */
+ a2 = g_strdup_printf (":xmpp/%s", realm);
+ a2h = sasl_md5_hex_hash (a2, strlen(a2));
+
+ kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s", a1h, nonce, cnonce, a2h);
+ g_free (sasl->digest_md5_rspauth);
+ sasl->digest_md5_rspauth = sasl_md5_hex_hash (kd, strlen(kd));
+
+ g_free (a1);
+ g_free (a1h);
+ g_free (a2);
+ g_free (a2h);
+ g_free (kd);
+
+out:
+ g_free (cnonce);
+ if (response) {
+ return g_string_free (response, FALSE);
+ } else {
+ return NULL;
+ }
+
+error:
+ g_string_free (response, TRUE);
+ response = NULL;
+ goto out;
+}
+
+static gboolean
+sasl_digest_md5_send_initial_response (LmSASL *sasl, GHashTable *challenge)
+{
+ LmMessage *msg;
+ gchar *response;
+ gchar *response64;
+ int result;
+
+ response = sasl_md5_prepare_response(sasl, challenge);
+ if (response == NULL) {
+ return FALSE;
+ }
+
+ response64 = base64_encode ((gchar *)response, strlen(response));
+
+ msg = lm_message_new (NULL, LM_MESSAGE_TYPE_RESPONSE);
+ lm_message_node_set_attributes (msg->node,
+ "xmlns", XMPP_NS_SASL_AUTH,
+ NULL);
+ lm_message_node_set_value (msg->node, response64);
+
+ result = lm_connection_send (sasl->connection, msg, NULL);
+
+ g_free (response);
+ g_free (response64);
+ lm_message_unref (msg);
+
+ if (!result) {
+ return FALSE;
+ }
+
+ sasl->state = SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE;
+
+ return TRUE;
+}
+
+static gboolean
+sasl_digest_md5_check_server_response(LmSASL *sasl, GHashTable *challenge)
+{
+ LmMessage *msg;
+ const gchar *rspauth;
+ int result;
+
+ rspauth = g_hash_table_lookup (challenge, "rspauth");
+ if (rspauth == NULL) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
+ "%s: server sent an invalid reply (no rspauth)\n",
+ G_STRFUNC);*/
+ lm_verbose("%s: server sent an invalid reply (no rspauth)\n",G_STRFUNC);
+
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ TRUE, "server error");
+ }
+ return FALSE;
+ }
+
+ if (strcmp (sasl->digest_md5_rspauth, rspauth) != 0) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
+ "%s: server sent an invalid reply (rspauth not matching)\n",
+ G_STRFUNC);*/
+ lm_verbose("%s: server sent an invalid reply (rspauth not matching)\n",
+ G_STRFUNC);
+
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ TRUE, "server error");
+ }
+ return FALSE;
+ }
+
+ msg = lm_message_new (NULL, LM_MESSAGE_TYPE_RESPONSE);
+ lm_message_node_set_attributes (msg->node,
+ "xmlns", XMPP_NS_SASL_AUTH,
+ NULL);
+
+ result = lm_connection_send (sasl->connection, msg, NULL);
+ lm_message_unref (msg);
+
+ if (!result) {
+ g_warning ("Failed to send SASL response\n");
+ return FALSE;
+ }
+
+ sasl->state = SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE;
+
+ return TRUE;
+}
+
+static gboolean
+sasl_digest_md5_handle_challenge (LmSASL *sasl, LmMessageNode *node)
+{
+ const gchar *encoded;
+ gchar *challenge;
+ gsize len;
+ GHashTable *h;
+
+ encoded = lm_message_node_get_value (node);
+ if (!encoded) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: got empty challenge!", G_STRFUNC);*/
+ lm_verbose("[sasl_digest_md5_handle_challenge]: %s: got empty challenge!", G_STRFUNC);
+ return FALSE;
+ }
+
+ challenge = (gchar *) base64_decode (encoded, &len);
+ h = sasl_digest_md5_challenge_to_hash (challenge);
+ g_free(challenge);
+
+ if (!h) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: server sent an invalid challenge", G_STRFUNC);*/
+ lm_verbose("[sasl_digest_md5_handle_challenge]: %s: server sent an invalid challenge", G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+ return FALSE;
+ }
+
+ switch (sasl->state) {
+ case SASL_AUTH_STATE_DIGEST_MD5_STARTED:
+ sasl_digest_md5_send_initial_response (sasl, h);
+ break;
+ case SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE:
+ sasl_digest_md5_check_server_response (sasl, h);
+ break;
+ default:
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: server sent a challenge at the wrong time",
+ G_STRFUNC);*/
+ lm_verbose( "[sasl_digst_md5_handle_challenge]: %s: server sent a challenge at the wrong time",
+ G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+
+ return FALSE;
+ }
+
+ g_hash_table_destroy(h);
+
+ return TRUE;
+}
+
+static LmHandlerResult
+sasl_challenge_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ LmSASL *sasl;
+ const gchar *ns;
+
+ ns = lm_message_node_get_attribute (message->node, "xmlns");
+ if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ sasl = (LmSASL *) user_data;
+
+ switch (sasl->auth_type) {
+ case AUTH_TYPE_PLAIN:
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
+ "%s: server sent challenge for PLAIN mechanism",
+ G_STRFUNC);*/
+ lm_verbose("[sasl_challenge_cb]: %s: server sent challenge for PLAIN mechanism",G_STRFUNC);
+
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+ break;
+ case AUTH_TYPE_DIGEST:
+ sasl_digest_md5_handle_challenge (sasl, message->node);
+ break;
+ default:
+ g_warning ("Wrong auth type");
+ break;
+ }
+ UNUSED_FORMAL_PARAM(handler);
+ UNUSED_FORMAL_PARAM(connection);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+static LmHandlerResult
+sasl_success_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ LmSASL *sasl;
+ const gchar *ns;
+
+ ns = lm_message_node_get_attribute (message->node, "xmlns");
+ if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ sasl = (LmSASL *) user_data;
+
+ switch (sasl->auth_type) {
+ case AUTH_TYPE_PLAIN:
+ if (sasl->state != SASL_AUTH_STATE_PLAIN_STARTED) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: server sent success before finishing auth",
+ G_STRFUNC);*/
+ lm_verbose("[sasl_success_cb]: %s: server sent success before finishing auth", G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+ }
+ break;
+ case AUTH_TYPE_DIGEST:
+ if (sasl->state != SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE &&
+ sasl->state != SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: server sent success before finishing auth",
+ G_STRFUNC);*/
+ lm_verbose("[sasl_success_cb]: %s: server sent success before finishing auth", G_STRFUNC);
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection,
+ FALSE, "server error");
+ }
+ }
+ break;
+ default:
+ g_warning ("Wrong auth type");
+ break;
+ }
+
+/* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: SASL authentication successful", G_STRFUNC);*/
+ lm_verbose("[sasl_success_cb]: %s: SASL authentication successful", G_STRFUNC);
+
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection, TRUE, NULL);
+ }
+ UNUSED_FORMAL_PARAM(handler);
+ UNUSED_FORMAL_PARAM(connection);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+
+}
+
+static LmHandlerResult
+sasl_failure_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ LmSASL *sasl;
+ const gchar *ns;
+ const gchar *reason = "unknown reason";
+ lm_verbose("[sasl_failure_cb]: inside sasl_failure_cb\n");
+ ns = lm_message_node_get_attribute (message->node, "xmlns");
+ if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+
+ sasl = (LmSASL *) user_data;
+
+ if (message->node->children) {
+ const gchar *r;
+
+ r = lm_message_node_get_value (message->node->children);
+ if (r) {
+ reason = r;
+ }
+ }
+ lm_verbose("[sasl_failure_cb]: %s: SASL authentication failed: %s\n", G_STRFUNC, reason);
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: SASL authentication failed: %s", G_STRFUNC, reason);*/
+
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection, FALSE, reason);
+ }
+ UNUSED_FORMAL_PARAM(handler);
+ UNUSED_FORMAL_PARAM(connection);
+ return LM_HANDLER_RESULT_REMOVE_MESSAGE;
+}
+
+
+static gboolean
+sasl_start (LmSASL *sasl)
+{
+ LmMessage *auth_msg;
+ gboolean result;
+ const char *mech = NULL;
+
+ auth_msg = lm_message_new (NULL, LM_MESSAGE_TYPE_AUTH);
+ lm_verbose("[sasl_start]: inside sasl_start\n");
+ if (sasl->auth_type == AUTH_TYPE_PLAIN) {
+ GString *str;
+ gchar *cstr;
+
+ str = g_string_new ("");
+
+ mech = "PLAIN";
+ sasl->state = SASL_AUTH_STATE_PLAIN_STARTED;
+ lm_verbose("[sasl_start]: inside sasl_start: auth type is PLAIN\n");
+ if (sasl->username == NULL || sasl->password == NULL) {
+ lm_verbose("[sasl_start]: no username or password provided\n");
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: no username or password provided",
+ G_STRFUNC);*/
+ if (sasl->handler) {
+ sasl->handler (sasl, sasl->connection, FALSE, "no username/password provided");
+ }
+
+ return FALSE;
+ }
+
+ g_string_append_c (str, '\0');
+ g_string_append (str, sasl->username);
+ g_string_append_c (str, '\0');
+ g_string_append (str, sasl->password);
+ cstr = base64_encode ((gchar *)str->str, str->len);
+
+ lm_message_node_set_value (auth_msg->node, cstr);
+
+ g_string_free (str, TRUE);
+ g_free (cstr);
+
+ /* Here we say the Google magic word. Bad Google. */
+ lm_message_node_set_attributes (auth_msg->node,
+ "xmlns:ga", "http://www.google.com/talk/protocol/auth",
+ "ga:client-uses-full-bind-result", "true",
+ NULL);
+
+ }
+ else if (sasl->auth_type == AUTH_TYPE_DIGEST) {
+ mech = "DIGEST-MD5";
+ sasl->state = SASL_AUTH_STATE_DIGEST_MD5_STARTED;
+ }
+
+ lm_message_node_set_attributes (auth_msg->node,
+ "xmlns", XMPP_NS_SASL_AUTH,
+ "mechanism", mech,
+ NULL);
+
+ result = lm_connection_send (sasl->connection, auth_msg, NULL);
+ lm_message_unref (auth_msg);
+
+ if (!result) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+sasl_set_auth_type (LmSASL *sasl, LmMessageNode *mechanisms)
+{
+ LmMessageNode *m;
+ const gchar *ns;
+
+ sasl->auth_type = 0;
+ lm_verbose("[sasl_set_auth_type]: inside sasl_set_auth_type\n");
+ ns = lm_message_node_get_attribute (mechanisms, "xmlns");
+ if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
+ return FALSE;
+ }
+
+ for (m = mechanisms->children; m; m = m->next) {
+ const gchar *name;
+
+ name = lm_message_node_get_value (m);
+
+ if (!name) {
+ continue;
+ }
+ if (strcmp (name, "PLAIN") == 0) {
+ sasl->auth_type |= AUTH_TYPE_PLAIN;
+ continue;
+ }
+ if (strcmp (name, "DIGEST-MD5") == 0) {
+ sasl->auth_type |= AUTH_TYPE_DIGEST;
+ continue;
+ }
+
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: unknown SASL auth mechanism: %s", G_STRFUNC, name);*/
+ lm_verbose ("[sasl_set_auth_type]: %s: unknown SASL auth mechanism: %s", G_STRFUNC, name);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+sasl_authenticate (LmSASL *sasl)
+{
+ lm_verbose ("[sasl_authenticate]: inside sasl_authenticate\n");
+ if (sasl->auth_type == 0) {
+ /*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
+ "%s: no supported SASL auth mechanisms found",
+ G_STRFUNC);*/
+ lm_verbose ("[sasl_authenticate]: %s: no supported SASL auth mechanisms found",G_STRFUNC);
+
+ return FALSE;
+ }
+
+ /* Prefer DIGEST */
+ if (sasl->auth_type & AUTH_TYPE_DIGEST) {
+ sasl->auth_type = AUTH_TYPE_DIGEST;
+ return sasl_start (sasl);
+ }
+ else if (sasl->auth_type & AUTH_TYPE_PLAIN) {
+ sasl->auth_type = AUTH_TYPE_PLAIN;
+ return sasl_start (sasl);
+ }
+
+ return FALSE;
+}
+
+static LmHandlerResult
+sasl_features_cb (LmMessageHandler *handler,
+ LmConnection *connection,
+ LmMessage *message,
+ gpointer user_data)
+{
+ LmMessageNode *mechanisms;
+ LmSASL *sasl;
+
+ lm_verbose ("\n[sasl_feature_cb]: Stream features received\n\n");
+ //g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL, "Stream features received\n");
+ mechanisms = lm_message_node_find_child (message->node, "mechanisms");
+ if (!mechanisms) {
+ gchar* childname = NULL;
+ lm_message_node_get_child(message->node,childname);
+ lm_verbose("[sasl_feature_cb]: Child name of stream:features %s\n", message->node->children->name);
+ lm_verbose("[sasl_feature_cb]: Child pointer of stream:features %u\n", message->node->children);
+ lm_verbose ("[sasl_feature_cb]: mechanisms empty:LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS \n");
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+ }
+ lm_verbose("[sasl_features_cb]: Mechanisms not null\n");
+
+ sasl = (LmSASL *) user_data;
+ sasl->features_received = TRUE;
+
+ sasl_set_auth_type (sasl, mechanisms);
+
+ if (sasl->start_auth) {
+ lm_verbose ("[sasl_feature_cb]: Going to call sasl_authenticate\n");
+ sasl_authenticate (sasl);
+ }
+ UNUSED_FORMAL_PARAM(handler);
+ UNUSED_FORMAL_PARAM(connection);
+ return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+}
+
+LmSASL *
+lm_sasl_new (LmConnection *connection)
+{
+ LmSASL *sasl;
+
+ sasl = g_new0 (LmSASL, 1);
+
+ sasl->connection = connection;
+ sasl->features_received = FALSE;
+ sasl->start_auth = FALSE;
+
+ sasl->features_cb = lm_message_handler_new (sasl_features_cb,
+ sasl,
+ NULL);
+ lm_verbose ("[lm_sasl_new]: inside \n");
+ lm_connection_register_message_handler (connection,
+ sasl->features_cb,
+ LM_MESSAGE_TYPE_STREAM_FEATURES,
+ LM_HANDLER_PRIORITY_LAST);
+ return sasl;
+}
+
+void
+lm_sasl_authenticate (LmSASL *sasl,
+ const gchar *username,
+ const gchar *password,
+ const gchar *server,
+ LmSASLResultHandler handler)
+{
+ sasl->username = g_strdup (username);
+ sasl->password = g_strdup (password);
+ sasl->server = g_strdup (server);
+ sasl->handler = handler;
+
+ sasl->challenge_cb = lm_message_handler_new (sasl_challenge_cb,
+ sasl,
+ NULL);
+ lm_connection_register_message_handler (sasl->connection,
+ sasl->challenge_cb,
+ LM_MESSAGE_TYPE_CHALLENGE,
+ LM_HANDLER_PRIORITY_FIRST);
+
+ sasl->success_cb = lm_message_handler_new (sasl_success_cb,
+ sasl,
+ NULL);
+ lm_connection_register_message_handler (sasl->connection,
+ sasl->success_cb,
+ LM_MESSAGE_TYPE_SUCCESS,
+ LM_HANDLER_PRIORITY_FIRST);
+
+ sasl->failure_cb = lm_message_handler_new (sasl_failure_cb,
+ sasl,
+ NULL);
+ lm_connection_register_message_handler (sasl->connection,
+ sasl->failure_cb,
+ LM_MESSAGE_TYPE_FAILURE,
+ LM_HANDLER_PRIORITY_FIRST);
+
+ if (sasl->features_received) {
+ lm_verbose("[lm_sasl_authenticate]: calling sasl_authenticate\n");
+ sasl_authenticate (sasl);
+ } else {
+ sasl->start_auth = TRUE;
+ }
+}
+
+void
+lm_sasl_free (LmSASL *sasl)
+{
+ g_return_if_fail (sasl != NULL);
+
+ g_free (sasl->username);
+ g_free (sasl->password);
+ g_free (sasl->server);
+
+ if (sasl->features_cb) {
+ lm_connection_unregister_message_handler (sasl->connection,
+ sasl->features_cb,
+ LM_MESSAGE_TYPE_STREAM_FEATURES);
+ }
+
+ if (sasl->challenge_cb) {
+ lm_connection_unregister_message_handler (sasl->connection,
+ sasl->challenge_cb,
+ LM_MESSAGE_TYPE_CHALLENGE);
+ }
+
+ if (sasl->success_cb) {
+ lm_connection_unregister_message_handler (sasl->connection,
+ sasl->success_cb,
+ LM_MESSAGE_TYPE_SUCCESS);
+ }
+
+ if (sasl->failure_cb) {
+ lm_connection_unregister_message_handler (sasl->connection,
+ sasl->failure_cb,
+ LM_MESSAGE_TYPE_FAILURE);
+ }
+
+ g_free (sasl);
+}
+
+
+void
+lm_sasl_get_auth_params (LmSASL *sasl, const gchar **username,
+ const gchar **password)
+{
+ *username = sasl->username;
+ *password = sasl->password;
+}
+