loudmouth/src/lm-sasl.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
       
     2 /*
       
     3  * Copyright (C) 2007 Collabora Ltd.
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public License as
       
     7  * published by the Free Software Foundation; either version 2 of the
       
     8  * License, or (at your option) any later version.
       
     9  *
       
    10  * This program is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Lesser General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this program; if not, write to the
       
    17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    18  * Boston, MA 02111-1307, USA.
       
    19  */
       
    20 
       
    21 #include <stdio.h>
       
    22 #include <string.h>
       
    23 #include <glib.h>
       
    24 
       
    25 //hack - to get over compilation error
       
    26 #include <glib_global.h>
       
    27 #include "lm-sock.h"
       
    28 #include "lm-debug.h"
       
    29 #include "lm-error.h"
       
    30 #include "lm-internals.h"
       
    31 #include "lm-message-queue.h"
       
    32 #include "lm-misc.h"
       
    33 #include "lm-ssl-internals.h"
       
    34 #include "lm-parser.h"
       
    35 #include "lm-sha.h"
       
    36 #include "lm-connection.h"
       
    37 #include "lm-utils.h"
       
    38 #include "lm-socket.h"
       
    39 #include "lm-sasl.h"
       
    40 
       
    41 #include "md5.h"
       
    42 #include "base64.h"
       
    43 
       
    44 typedef enum {
       
    45 	AUTH_TYPE_PLAIN  = 1,
       
    46 	AUTH_TYPE_DIGEST = 2
       
    47 } AuthType;
       
    48 
       
    49 typedef enum {
       
    50 	SASL_AUTH_STATE_NO_MECH,
       
    51 	SASL_AUTH_STATE_PLAIN_STARTED,
       
    52 	SASL_AUTH_STATE_DIGEST_MD5_STARTED,
       
    53 	SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE,
       
    54 	SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE,
       
    55 } SaslAuthState;
       
    56 
       
    57 struct _LmSASL {
       
    58 	LmConnection        *connection;
       
    59 	AuthType             auth_type;
       
    60 	SaslAuthState        state;
       
    61 	gchar               *username;
       
    62 	gchar               *password;
       
    63 	gchar               *server;
       
    64 	gchar               *digest_md5_rspauth;
       
    65 	LmMessageHandler    *features_cb;
       
    66 	LmMessageHandler    *challenge_cb;
       
    67 	LmMessageHandler    *success_cb;
       
    68 	LmMessageHandler    *failure_cb;
       
    69 
       
    70 	gboolean             features_received;
       
    71 	gboolean             start_auth;
       
    72 
       
    73 	LmSASLResultHandler  handler;
       
    74 };
       
    75 
       
    76 #define XMPP_NS_SASL_AUTH "urn:ietf:params:xml:ns:xmpp-sasl"
       
    77 
       
    78 static LmHandlerResult     sasl_features_cb  (LmMessageHandler *handler,
       
    79 					      LmConnection     *connection,
       
    80 					      LmMessage        *message,
       
    81 					      gpointer          user_data);
       
    82 
       
    83 static LmHandlerResult     sasl_challenge_cb (LmMessageHandler *handler,
       
    84 					      LmConnection     *connection,
       
    85 					      LmMessage        *message,
       
    86 					      gpointer          user_data);
       
    87 
       
    88 static LmHandlerResult     sasl_success_cb   (LmMessageHandler *handler,
       
    89 					      LmConnection     *connection,
       
    90 					      LmMessage        *message,
       
    91 					      gpointer          user_data);
       
    92 
       
    93 static LmHandlerResult     sasl_failure_cb   (LmMessageHandler *handler,
       
    94 					      LmConnection     *connection,
       
    95 					      LmMessage        *message,
       
    96 					      gpointer          user_data);
       
    97 
       
    98 
       
    99 /* DIGEST-MD5 mechanism code from libgibber */
       
   100 
       
   101 static gchar *
       
   102 sasl_strndup_unescaped (const gchar *str, gsize len) 
       
   103 {
       
   104 	const gchar *s;
       
   105 	gchar       *d;
       
   106 	gchar       *ret;
       
   107 
       
   108 	ret = g_malloc0 (len + 1);
       
   109 	for (s = str, d = ret ; s < (str + len) ; s++, d++) {
       
   110 		if (*s == '\\') s++;
       
   111 		*d = *s;
       
   112 	}
       
   113 
       
   114 	return ret;
       
   115 }
       
   116 
       
   117 static GHashTable *
       
   118 sasl_digest_md5_challenge_to_hash (const gchar * challenge)
       
   119 {
       
   120 	const gchar *keystart, *keyend, *valstart;
       
   121 	const gchar *c = challenge;
       
   122 	gchar       *key, *val;
       
   123 	GHashTable  *result;
       
   124 	
       
   125 	result = g_hash_table_new_full (g_str_hash, g_str_equal, 
       
   126 					g_free, g_free);
       
   127 
       
   128 	do { 
       
   129 		while (g_ascii_isspace(*c)) c++;
       
   130 
       
   131 		keystart = c;
       
   132 		for (; *c != '\0' && *c != '='; c++);
       
   133 
       
   134 		if (*c == '\0' || c == keystart) goto error;
       
   135 
       
   136 		keyend = c; 
       
   137 		c++;
       
   138 
       
   139 		if (*c == '"') {
       
   140 			c++;
       
   141 			valstart = c;
       
   142 			for (; *c != '\0' && *c != '"'; c++);
       
   143 			if (*c == '\0' || c == valstart) goto error;
       
   144 			val = sasl_strndup_unescaped (valstart, c - valstart);
       
   145 			c++;
       
   146 		} else {
       
   147 			valstart = c;
       
   148 			for (; *c !=  '\0' && *c != ','; c++);
       
   149 			if (c == valstart) goto error;
       
   150 			val = g_strndup (valstart, c - valstart);
       
   151 		}
       
   152 
       
   153 		key = g_strndup (keystart, keyend - keystart);
       
   154 
       
   155 		g_hash_table_insert (result, key, val);
       
   156 
       
   157 		if (*c == ',') c++;
       
   158 	} while (*c != '\0');
       
   159 
       
   160 	return result;
       
   161 error:
       
   162 	/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL, 
       
   163 	       "Failed to parse challenge: %s", challenge);*/
       
   164 	lm_verbose("[sasl_digest_md5_challenge_to_hash]:Failed to parse challenge: %s", challenge);
       
   165 	g_hash_table_destroy (result);
       
   166 	return NULL;
       
   167 }
       
   168 
       
   169 static gchar *
       
   170 sasl_md5_hex_hash (gchar *value, gsize len) 
       
   171 {
       
   172 	md5_byte_t   digest_md5[16];
       
   173 	md5_state_t  md5_calc;
       
   174 	GString     *str;
       
   175 	int          i;
       
   176 
       
   177 	str = g_string_sized_new (32);
       
   178 
       
   179 	md5_init (&md5_calc);
       
   180 	md5_append (&md5_calc, (const md5_byte_t *)value, len);
       
   181 	md5_finish (&md5_calc, digest_md5);
       
   182 
       
   183 	for (i = 0 ; i < 16 ; i++) {
       
   184 		g_string_append_printf (str, "%02x", digest_md5[i]);
       
   185 	}
       
   186 
       
   187 	return g_string_free (str, FALSE);
       
   188 }
       
   189 
       
   190 static gchar *
       
   191 sasl_digest_md5_generate_cnonce(void)
       
   192 {
       
   193 	/* RFC 2831 recommends the the nonce to be either hexadecimal or base64 with
       
   194 	 * at least 64 bits of entropy */
       
   195 #define NR 8
       
   196 	guint32 n[NR]; 
       
   197 	int i;
       
   198 
       
   199 	for (i = 0; i < NR; i++) {
       
   200 		n[i] = g_random_int();
       
   201 	}
       
   202 
       
   203 	return base64_encode ((gchar *)n, sizeof(n));
       
   204 }
       
   205 
       
   206 static gchar *
       
   207 sasl_md5_prepare_response (LmSASL *sasl, GHashTable *challenge)
       
   208 {
       
   209 	GString     *response;
       
   210 	const gchar *realm, *nonce;
       
   211 	gchar       *a1, *a1h, *a2, *a2h, *kd, *kdh;
       
   212 	gchar       *cnonce = NULL;
       
   213 	gchar       *tmp;
       
   214 	md5_byte_t   digest_md5[16];
       
   215 	md5_state_t  md5_calc;
       
   216 	gsize        len;
       
   217 
       
   218 	response = g_string_new ("");
       
   219 
       
   220 	if (sasl->username == NULL || sasl->password == NULL) {
       
   221 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   222 		       "%s: no username or password provided", G_STRFUNC);*/
       
   223 		lm_verbose("[sasl_md5_prepare_response]: %s: no username or password provided", G_STRFUNC);
       
   224 		if (sasl->handler) {
       
   225 			sasl->handler (sasl, sasl->connection, 
       
   226 				       FALSE, "no username/password provided");
       
   227 		}
       
   228 		goto error;
       
   229 	}
       
   230 
       
   231 	nonce = g_hash_table_lookup (challenge, "nonce");
       
   232 	if (nonce == NULL || nonce == '\0') {
       
   233 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   234 		       "%s: server didn't provide a nonce in the challenge", 
       
   235 		       G_STRFUNC);*/
       
   236 		lm_verbose("[sasl_md5_prepare_response]: %s: server didn't provide a nonce in the challenge", G_STRFUNC);
       
   237 		if (sasl->handler) {
       
   238 			sasl->handler (sasl, sasl->connection,
       
   239 				       FALSE, "server error");
       
   240 		}
       
   241 		goto error;
       
   242 	}
       
   243 
       
   244 	cnonce = sasl_digest_md5_generate_cnonce ();
       
   245 
       
   246 	/* FIXME challenge can contain multiple realms */
       
   247 	realm = g_hash_table_lookup (challenge, "realm");
       
   248 	if (realm == NULL) {
       
   249 		realm = sasl->server;
       
   250 	}
       
   251 
       
   252 	/* FIXME properly escape values */
       
   253 	g_string_append_printf (response, "username=\"%s\"", sasl->username);
       
   254 	g_string_append_printf (response, ",realm=\"%s\"", realm);
       
   255 	g_string_append_printf (response, ",digest-uri=\"xmpp/%s\"", realm);
       
   256 	g_string_append_printf (response, ",nonce=\"%s\",nc=00000001", nonce);
       
   257 	g_string_append_printf (response, ",cnonce=\"%s\"", cnonce);
       
   258 	/* FIXME should check if auth is in the cop challenge val */
       
   259 	g_string_append_printf (response, ",qop=auth,charset=utf-8");
       
   260 
       
   261 	tmp = g_strdup_printf ("%s:%s:%s", 
       
   262 			       sasl->username, realm, sasl->password);
       
   263 	md5_init (&md5_calc);
       
   264 	md5_append (&md5_calc, (const md5_byte_t *)tmp, strlen(tmp));
       
   265 	md5_finish (&md5_calc, digest_md5);
       
   266 	g_free (tmp);
       
   267 
       
   268 	a1 = g_strdup_printf ("0123456789012345:%s:%s", nonce, cnonce);
       
   269 	len = strlen (a1);
       
   270 	memcpy (a1, digest_md5, 16);
       
   271 	a1h = sasl_md5_hex_hash (a1, len);
       
   272 
       
   273 	a2 = g_strdup_printf ("AUTHENTICATE:xmpp/%s", realm);
       
   274 	a2h = sasl_md5_hex_hash (a2, strlen(a2));
       
   275 
       
   276 	kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s",
       
   277 			      a1h, nonce, cnonce, a2h);
       
   278 	kdh = sasl_md5_hex_hash (kd, strlen(kd));
       
   279 	g_string_append_printf (response, ",response=%s", kdh);
       
   280 
       
   281 	g_free (kd);
       
   282 	g_free (kdh);
       
   283 	g_free (a2);
       
   284 	g_free (a2h);
       
   285 
       
   286 	/* Calculate the response we expect from the server */
       
   287 	a2 = g_strdup_printf (":xmpp/%s", realm);
       
   288 	a2h = sasl_md5_hex_hash (a2, strlen(a2));
       
   289 
       
   290 	kd = g_strdup_printf ("%s:%s:00000001:%s:auth:%s", a1h, nonce, cnonce, a2h);
       
   291 	g_free (sasl->digest_md5_rspauth);
       
   292 	sasl->digest_md5_rspauth = sasl_md5_hex_hash (kd, strlen(kd));
       
   293 
       
   294 	g_free (a1);
       
   295 	g_free (a1h);
       
   296 	g_free (a2);
       
   297 	g_free (a2h);
       
   298 	g_free (kd);
       
   299 
       
   300 out:
       
   301 	g_free (cnonce);
       
   302 	if (response) {
       
   303 		return g_string_free (response, FALSE);
       
   304 	} else {
       
   305 		return NULL;
       
   306 	}
       
   307 
       
   308 error:
       
   309 	g_string_free (response, TRUE);
       
   310 	response = NULL;
       
   311 	goto out;
       
   312 }
       
   313 
       
   314 static gboolean
       
   315 sasl_digest_md5_send_initial_response (LmSASL *sasl, GHashTable *challenge)
       
   316 {
       
   317 	LmMessage *msg;
       
   318 	gchar     *response;
       
   319 	gchar     *response64;
       
   320 	int        result;
       
   321 
       
   322 	response = sasl_md5_prepare_response(sasl, challenge);
       
   323 	if (response == NULL) {
       
   324 		return FALSE;
       
   325 	}
       
   326 
       
   327 	response64 = base64_encode ((gchar *)response, strlen(response));
       
   328 
       
   329 	msg = lm_message_new (NULL, LM_MESSAGE_TYPE_RESPONSE);
       
   330 	lm_message_node_set_attributes (msg->node,
       
   331 					"xmlns", XMPP_NS_SASL_AUTH,
       
   332 					NULL);
       
   333 	lm_message_node_set_value (msg->node, response64);
       
   334 
       
   335 	result = lm_connection_send (sasl->connection, msg, NULL);
       
   336 
       
   337 	g_free (response);
       
   338 	g_free (response64);
       
   339 	lm_message_unref (msg);
       
   340 
       
   341 	if (!result) {
       
   342 		return FALSE;
       
   343 	}
       
   344 
       
   345 	sasl->state = SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE;
       
   346 
       
   347 	return TRUE;
       
   348 }
       
   349 
       
   350 static gboolean
       
   351 sasl_digest_md5_check_server_response(LmSASL *sasl, GHashTable *challenge)
       
   352 {
       
   353 	LmMessage   *msg;
       
   354 	const gchar *rspauth;
       
   355 	int          result;
       
   356 
       
   357 	rspauth = g_hash_table_lookup (challenge, "rspauth");
       
   358 	if (rspauth == NULL) {
       
   359 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
       
   360 		       "%s: server sent an invalid reply (no rspauth)\n",
       
   361 		       G_STRFUNC);*/
       
   362 		lm_verbose("%s: server sent an invalid reply (no rspauth)\n",G_STRFUNC);
       
   363 
       
   364 		if (sasl->handler) {
       
   365 			sasl->handler (sasl, sasl->connection, 
       
   366 				       TRUE, "server error");
       
   367 		}
       
   368 		return FALSE;
       
   369 	}
       
   370 
       
   371 	if (strcmp (sasl->digest_md5_rspauth, rspauth) != 0) {
       
   372 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
       
   373 		       "%s: server sent an invalid reply (rspauth not matching)\n", 
       
   374 		       G_STRFUNC);*/
       
   375 		lm_verbose("%s: server sent an invalid reply (rspauth not matching)\n", 
       
   376 		       G_STRFUNC);
       
   377 
       
   378 		if (sasl->handler) {
       
   379 			sasl->handler (sasl, sasl->connection,
       
   380 				       TRUE, "server error");
       
   381 		}
       
   382 		return FALSE;
       
   383 	}
       
   384 
       
   385 	msg = lm_message_new (NULL, LM_MESSAGE_TYPE_RESPONSE);
       
   386 	lm_message_node_set_attributes (msg->node,
       
   387 					"xmlns", XMPP_NS_SASL_AUTH,
       
   388 					NULL);
       
   389 
       
   390 	result = lm_connection_send (sasl->connection, msg, NULL);
       
   391 	lm_message_unref (msg);
       
   392 
       
   393 	if (!result) {
       
   394 		g_warning ("Failed to send SASL response\n");
       
   395 		return FALSE;
       
   396 	}
       
   397 
       
   398 	sasl->state = SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE;
       
   399 
       
   400 	return TRUE;
       
   401 }
       
   402 
       
   403 static gboolean
       
   404 sasl_digest_md5_handle_challenge (LmSASL *sasl, LmMessageNode *node)
       
   405 {
       
   406 	const gchar *encoded;
       
   407 	gchar       *challenge;
       
   408 	gsize        len;
       
   409 	GHashTable  *h;
       
   410 
       
   411 	encoded = lm_message_node_get_value (node);
       
   412 	if (!encoded) {
       
   413 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   414 		       "%s: got empty challenge!", G_STRFUNC);*/
       
   415 		lm_verbose("[sasl_digest_md5_handle_challenge]: %s: got empty challenge!", G_STRFUNC);
       
   416 		return FALSE;
       
   417 	}
       
   418 
       
   419 	challenge = (gchar *) base64_decode (encoded, &len);
       
   420 	h = sasl_digest_md5_challenge_to_hash (challenge);
       
   421 	g_free(challenge);
       
   422 
       
   423 	if (!h) {
       
   424 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   425 		       "%s: server sent an invalid challenge", G_STRFUNC);*/
       
   426 		lm_verbose("[sasl_digest_md5_handle_challenge]: %s: server sent an invalid challenge", G_STRFUNC);
       
   427 		if (sasl->handler) {
       
   428 			sasl->handler (sasl, sasl->connection, 
       
   429 				       FALSE, "server error");
       
   430 		}
       
   431 		return FALSE;
       
   432 	}
       
   433 
       
   434 	switch (sasl->state) {
       
   435 	case SASL_AUTH_STATE_DIGEST_MD5_STARTED:
       
   436 		sasl_digest_md5_send_initial_response (sasl, h); 
       
   437 		break;
       
   438 	case SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE:
       
   439 		sasl_digest_md5_check_server_response (sasl, h); 
       
   440 		break;
       
   441 	default:
       
   442 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   443 		       "%s: server sent a challenge at the wrong time", 
       
   444 		       G_STRFUNC);*/
       
   445 		lm_verbose( "[sasl_digst_md5_handle_challenge]: %s: server sent a challenge at the wrong time", 
       
   446 		       G_STRFUNC);
       
   447 		if (sasl->handler) {
       
   448 			sasl->handler (sasl, sasl->connection,
       
   449 				       FALSE, "server error");
       
   450 		}
       
   451 
       
   452 		return FALSE;
       
   453 	} 
       
   454 
       
   455 	g_hash_table_destroy(h);
       
   456 
       
   457 	return TRUE;
       
   458 }
       
   459 
       
   460 static LmHandlerResult
       
   461 sasl_challenge_cb (LmMessageHandler *handler,
       
   462 		   LmConnection     *connection,
       
   463 		   LmMessage        *message,
       
   464 		   gpointer          user_data)
       
   465 {
       
   466 	LmSASL      *sasl;
       
   467 	const gchar *ns;
       
   468 
       
   469 	ns = lm_message_node_get_attribute (message->node, "xmlns");
       
   470 	if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
       
   471 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   472 	}
       
   473 
       
   474 	sasl = (LmSASL *) user_data;
       
   475 
       
   476 	switch (sasl->auth_type) {
       
   477 	case AUTH_TYPE_PLAIN:
       
   478 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
       
   479 		       "%s: server sent challenge for PLAIN mechanism",
       
   480 		       G_STRFUNC);*/
       
   481 		lm_verbose("[sasl_challenge_cb]: %s: server sent challenge for PLAIN mechanism",G_STRFUNC);
       
   482 
       
   483 		if (sasl->handler) {
       
   484 			sasl->handler (sasl, sasl->connection, 
       
   485 				       FALSE, "server error");
       
   486 		}
       
   487 		break;
       
   488 	case AUTH_TYPE_DIGEST:
       
   489 		sasl_digest_md5_handle_challenge (sasl, message->node);
       
   490 		break;
       
   491 	default:
       
   492 		g_warning ("Wrong auth type");
       
   493 		break;
       
   494 	}
       
   495 	UNUSED_FORMAL_PARAM(handler);
       
   496 	UNUSED_FORMAL_PARAM(connection);
       
   497 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   498 }
       
   499 
       
   500 static LmHandlerResult
       
   501 sasl_success_cb (LmMessageHandler *handler,
       
   502 		 LmConnection     *connection,
       
   503 		 LmMessage        *message,
       
   504 		 gpointer          user_data)
       
   505 {
       
   506 	LmSASL      *sasl;
       
   507 	const gchar *ns;
       
   508 	
       
   509 	ns = lm_message_node_get_attribute (message->node, "xmlns");
       
   510 	if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
       
   511 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   512 	}
       
   513 
       
   514 	sasl = (LmSASL *) user_data;
       
   515 
       
   516 	switch (sasl->auth_type) {
       
   517 	case AUTH_TYPE_PLAIN:
       
   518 		if (sasl->state != SASL_AUTH_STATE_PLAIN_STARTED) {
       
   519 			/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   520 			       "%s: server sent success before finishing auth", 
       
   521 			       G_STRFUNC);*/
       
   522 			lm_verbose("[sasl_success_cb]: %s: server sent success before finishing auth", G_STRFUNC);
       
   523 			if (sasl->handler) {
       
   524 				sasl->handler (sasl, sasl->connection, 
       
   525 					       FALSE, "server error");
       
   526 			}
       
   527 		}
       
   528 		break;
       
   529 	case AUTH_TYPE_DIGEST:
       
   530 		if (sasl->state != SASL_AUTH_STATE_DIGEST_MD5_SENT_AUTH_RESPONSE &&
       
   531 		    sasl->state != SASL_AUTH_STATE_DIGEST_MD5_SENT_FINAL_RESPONSE) {
       
   532 			/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   533 			       "%s: server sent success before finishing auth", 
       
   534 			       G_STRFUNC);*/
       
   535 			lm_verbose("[sasl_success_cb]: %s: server sent success before finishing auth", G_STRFUNC);
       
   536 			if (sasl->handler) {
       
   537 				sasl->handler (sasl, sasl->connection, 
       
   538 					       FALSE, "server error");
       
   539 			}
       
   540 		}
       
   541 		break;
       
   542 	default:
       
   543 		g_warning ("Wrong auth type");
       
   544 		break;
       
   545 	}
       
   546 
       
   547 /*	g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   548 	       "%s: SASL authentication successful", G_STRFUNC);*/
       
   549 	lm_verbose("[sasl_success_cb]: %s: SASL authentication successful", G_STRFUNC);
       
   550 
       
   551 	if (sasl->handler) {
       
   552 		sasl->handler (sasl, sasl->connection, TRUE, NULL);
       
   553 	}
       
   554 	UNUSED_FORMAL_PARAM(handler);
       
   555 	UNUSED_FORMAL_PARAM(connection);
       
   556 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   557 	
       
   558 }
       
   559 
       
   560 static LmHandlerResult
       
   561 sasl_failure_cb (LmMessageHandler *handler,
       
   562 		 LmConnection     *connection,
       
   563 		 LmMessage        *message,
       
   564 		 gpointer          user_data)
       
   565 {
       
   566 	LmSASL      *sasl;
       
   567 	const gchar *ns;
       
   568 	const gchar *reason = "unknown reason";
       
   569 	lm_verbose("[sasl_failure_cb]: inside sasl_failure_cb\n");
       
   570 	ns = lm_message_node_get_attribute (message->node, "xmlns");
       
   571 	if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
       
   572 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   573 	}
       
   574 
       
   575 	sasl = (LmSASL *) user_data;
       
   576 
       
   577 	if (message->node->children) {
       
   578 		const gchar *r;
       
   579 		
       
   580 		r = lm_message_node_get_value (message->node->children);
       
   581 		if (r) {
       
   582 			reason = r;
       
   583 		}
       
   584 	}
       
   585 	lm_verbose("[sasl_failure_cb]: %s: SASL authentication failed: %s\n", G_STRFUNC, reason);
       
   586 	/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   587 	       "%s: SASL authentication failed: %s", G_STRFUNC, reason);*/
       
   588 
       
   589 	if (sasl->handler) {
       
   590 		sasl->handler (sasl, sasl->connection, FALSE, reason);
       
   591 	}
       
   592 	UNUSED_FORMAL_PARAM(handler);
       
   593 	UNUSED_FORMAL_PARAM(connection);
       
   594 	return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
   595 }
       
   596 
       
   597 
       
   598 static gboolean
       
   599 sasl_start (LmSASL *sasl)
       
   600 {
       
   601 	LmMessage  *auth_msg;
       
   602 	gboolean    result;
       
   603 	const char *mech = NULL;
       
   604 
       
   605 	auth_msg = lm_message_new (NULL, LM_MESSAGE_TYPE_AUTH);
       
   606 	lm_verbose("[sasl_start]: inside sasl_start\n");
       
   607 	if (sasl->auth_type == AUTH_TYPE_PLAIN) {
       
   608       		GString *str;
       
   609 		gchar   *cstr;
       
   610 
       
   611 		str = g_string_new ("");
       
   612 
       
   613 		mech = "PLAIN";
       
   614 		sasl->state = SASL_AUTH_STATE_PLAIN_STARTED;
       
   615 		lm_verbose("[sasl_start]: inside sasl_start: auth type is PLAIN\n");
       
   616 		if (sasl->username == NULL || sasl->password == NULL) {
       
   617 			lm_verbose("[sasl_start]: no username or password provided\n");
       
   618 			/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   619 			       "%s: no username or password provided", 
       
   620 			       G_STRFUNC);*/
       
   621 			if (sasl->handler) {
       
   622 				sasl->handler (sasl, sasl->connection, FALSE, "no username/password provided");
       
   623 			}
       
   624 
       
   625 			return FALSE;
       
   626 		}
       
   627 
       
   628 		g_string_append_c (str, '\0');
       
   629 		g_string_append (str, sasl->username);
       
   630 		g_string_append_c (str, '\0');
       
   631 		g_string_append (str, sasl->password);
       
   632 		cstr = base64_encode ((gchar *)str->str, str->len);
       
   633 
       
   634 		lm_message_node_set_value (auth_msg->node, cstr);
       
   635 
       
   636 		g_string_free (str, TRUE);
       
   637 		g_free (cstr);
       
   638 
       
   639 		/* Here we say the Google magic word. Bad Google. */
       
   640 		lm_message_node_set_attributes (auth_msg->node,
       
   641 						"xmlns:ga", "http://www.google.com/talk/protocol/auth",
       
   642 						"ga:client-uses-full-bind-result", "true",
       
   643 						NULL);
       
   644 
       
   645 	} 
       
   646 	else if (sasl->auth_type == AUTH_TYPE_DIGEST) {
       
   647 		mech = "DIGEST-MD5";
       
   648 		sasl->state = SASL_AUTH_STATE_DIGEST_MD5_STARTED;
       
   649 	}
       
   650 
       
   651 	lm_message_node_set_attributes (auth_msg->node,
       
   652 					"xmlns", XMPP_NS_SASL_AUTH,
       
   653 					"mechanism", mech,
       
   654 					NULL);
       
   655 
       
   656 	result = lm_connection_send (sasl->connection, auth_msg, NULL);
       
   657 	lm_message_unref (auth_msg);
       
   658 
       
   659 	if (!result) {
       
   660 		return FALSE;
       
   661 	}
       
   662 
       
   663 	return TRUE;
       
   664 }
       
   665 
       
   666 static gboolean
       
   667 sasl_set_auth_type (LmSASL *sasl, LmMessageNode *mechanisms)
       
   668 {
       
   669 	LmMessageNode *m;
       
   670 	const gchar   *ns;
       
   671 
       
   672 	sasl->auth_type = 0;
       
   673 	lm_verbose("[sasl_set_auth_type]: inside sasl_set_auth_type\n");
       
   674 	ns = lm_message_node_get_attribute (mechanisms, "xmlns");
       
   675 	if (!ns || strcmp (ns, XMPP_NS_SASL_AUTH) != 0) {
       
   676 		return FALSE;
       
   677 	}
       
   678 
       
   679 	for (m = mechanisms->children; m; m = m->next) {
       
   680 		const gchar *name;
       
   681 		
       
   682 		name = lm_message_node_get_value (m);
       
   683 
       
   684 		if (!name) {
       
   685 			continue;
       
   686 		}
       
   687 		if (strcmp (name, "PLAIN") == 0) {
       
   688 			sasl->auth_type |= AUTH_TYPE_PLAIN;
       
   689 			continue;
       
   690 		}
       
   691 		if (strcmp (name, "DIGEST-MD5") == 0) {
       
   692 			sasl->auth_type |= AUTH_TYPE_DIGEST;
       
   693 			continue;
       
   694 		}
       
   695 
       
   696 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   697 		       "%s: unknown SASL auth mechanism: %s", G_STRFUNC, name);*/
       
   698 		lm_verbose ("[sasl_set_auth_type]: %s: unknown SASL auth mechanism: %s", G_STRFUNC, name);
       
   699 	}
       
   700 
       
   701 	return TRUE;
       
   702 }
       
   703 
       
   704 static gboolean
       
   705 sasl_authenticate (LmSASL *sasl)
       
   706 {
       
   707 	lm_verbose ("[sasl_authenticate]: inside sasl_authenticate\n");
       
   708 	if (sasl->auth_type == 0) {
       
   709 		/*g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL,
       
   710 		      "%s: no supported SASL auth mechanisms found",
       
   711 		      G_STRFUNC);*/
       
   712 	  lm_verbose ("[sasl_authenticate]: %s: no supported SASL auth mechanisms found",G_STRFUNC);
       
   713 
       
   714 		return FALSE;
       
   715 	}
       
   716 
       
   717 	/* Prefer DIGEST */
       
   718 	if (sasl->auth_type & AUTH_TYPE_DIGEST) {
       
   719 		sasl->auth_type = AUTH_TYPE_DIGEST;
       
   720 		return sasl_start (sasl);
       
   721 	}
       
   722 	else if (sasl->auth_type & AUTH_TYPE_PLAIN) {
       
   723 		sasl->auth_type = AUTH_TYPE_PLAIN;
       
   724 		return sasl_start (sasl);
       
   725 	} 
       
   726 
       
   727 	return FALSE;
       
   728 }
       
   729 
       
   730 static LmHandlerResult
       
   731 sasl_features_cb (LmMessageHandler *handler,
       
   732 		  LmConnection     *connection,
       
   733 		  LmMessage        *message,
       
   734 		  gpointer          user_data)
       
   735 {
       
   736     	LmMessageNode *mechanisms;
       
   737 	LmSASL        *sasl;
       
   738 
       
   739 	lm_verbose ("\n[sasl_feature_cb]: Stream features received\n\n");
       
   740 	//g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SASL, "Stream features received\n");
       
   741 	mechanisms = lm_message_node_find_child (message->node, "mechanisms");
       
   742 	if (!mechanisms) {
       
   743 	    gchar* childname = NULL;
       
   744 		lm_message_node_get_child(message->node,childname);
       
   745 		lm_verbose("[sasl_feature_cb]: Child name of stream:features %s\n", message->node->children->name);
       
   746 		lm_verbose("[sasl_feature_cb]: Child pointer of stream:features %u\n", message->node->children);
       
   747 		lm_verbose ("[sasl_feature_cb]: mechanisms empty:LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS \n");
       
   748 		return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   749 	}
       
   750 	lm_verbose("[sasl_features_cb]: Mechanisms not null\n");
       
   751 
       
   752 	sasl = (LmSASL *) user_data;
       
   753 	sasl->features_received = TRUE;
       
   754 
       
   755 	sasl_set_auth_type (sasl, mechanisms);
       
   756 
       
   757 	if (sasl->start_auth) {
       
   758 		lm_verbose ("[sasl_feature_cb]: Going to call sasl_authenticate\n");
       
   759 		sasl_authenticate (sasl);
       
   760 	}
       
   761 	UNUSED_FORMAL_PARAM(handler);
       
   762 	UNUSED_FORMAL_PARAM(connection);
       
   763 	return LM_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
       
   764 }
       
   765 
       
   766 LmSASL *
       
   767 lm_sasl_new (LmConnection *connection)
       
   768 {
       
   769 	LmSASL *sasl;
       
   770 	
       
   771 	sasl = g_new0 (LmSASL, 1);
       
   772 
       
   773 	sasl->connection = connection;
       
   774 	sasl->features_received = FALSE;
       
   775 	sasl->start_auth = FALSE;
       
   776 
       
   777 	sasl->features_cb = lm_message_handler_new (sasl_features_cb,
       
   778 						    sasl,
       
   779 						    NULL);
       
   780 	lm_verbose ("[lm_sasl_new]: inside \n");
       
   781 	lm_connection_register_message_handler (connection,
       
   782 						sasl->features_cb,
       
   783 						LM_MESSAGE_TYPE_STREAM_FEATURES,
       
   784 						LM_HANDLER_PRIORITY_LAST);
       
   785 	return sasl;
       
   786 }
       
   787 
       
   788 void
       
   789 lm_sasl_authenticate (LmSASL              *sasl,
       
   790 		      const gchar         *username,
       
   791 		      const gchar         *password,
       
   792 		      const gchar         *server,
       
   793 		      LmSASLResultHandler  handler)
       
   794 {
       
   795 	sasl->username   = g_strdup (username);
       
   796 	sasl->password   = g_strdup (password);
       
   797 	sasl->server     = g_strdup (server);
       
   798 	sasl->handler    = handler;
       
   799 
       
   800 	sasl->challenge_cb = lm_message_handler_new (sasl_challenge_cb,
       
   801 						     sasl,
       
   802 						     NULL);
       
   803 	lm_connection_register_message_handler (sasl->connection,
       
   804 						sasl->challenge_cb,
       
   805 						LM_MESSAGE_TYPE_CHALLENGE,
       
   806 						LM_HANDLER_PRIORITY_FIRST);
       
   807 	
       
   808 	sasl->success_cb = lm_message_handler_new (sasl_success_cb,
       
   809 						   sasl,
       
   810 						   NULL);
       
   811 	lm_connection_register_message_handler (sasl->connection,
       
   812 						sasl->success_cb,
       
   813 						LM_MESSAGE_TYPE_SUCCESS,
       
   814 						LM_HANDLER_PRIORITY_FIRST);
       
   815 
       
   816 	sasl->failure_cb = lm_message_handler_new (sasl_failure_cb,
       
   817 						   sasl,
       
   818 						   NULL);
       
   819 	lm_connection_register_message_handler (sasl->connection,
       
   820 						sasl->failure_cb,
       
   821 						LM_MESSAGE_TYPE_FAILURE,
       
   822 						LM_HANDLER_PRIORITY_FIRST);
       
   823 
       
   824 	if (sasl->features_received) {
       
   825 		lm_verbose("[lm_sasl_authenticate]: calling sasl_authenticate\n");
       
   826 		sasl_authenticate (sasl);
       
   827 	} else {
       
   828 		sasl->start_auth = TRUE;
       
   829 	}
       
   830 }
       
   831 
       
   832 void
       
   833 lm_sasl_free (LmSASL *sasl)
       
   834 {
       
   835 	g_return_if_fail (sasl != NULL);
       
   836 
       
   837 	g_free (sasl->username);
       
   838 	g_free (sasl->password);
       
   839 	g_free (sasl->server);
       
   840 
       
   841 	if (sasl->features_cb) {
       
   842 		lm_connection_unregister_message_handler (sasl->connection,
       
   843 							  sasl->features_cb, 
       
   844 							  LM_MESSAGE_TYPE_STREAM_FEATURES);
       
   845 	}
       
   846 
       
   847 	if (sasl->challenge_cb) {
       
   848 		lm_connection_unregister_message_handler (sasl->connection,
       
   849 							  sasl->challenge_cb,
       
   850 							  LM_MESSAGE_TYPE_CHALLENGE);
       
   851 	}
       
   852 
       
   853 	if (sasl->success_cb) {
       
   854 		lm_connection_unregister_message_handler (sasl->connection,
       
   855 							  sasl->success_cb,
       
   856 							  LM_MESSAGE_TYPE_SUCCESS);
       
   857 	}
       
   858 
       
   859 	if (sasl->failure_cb) {
       
   860 		lm_connection_unregister_message_handler (sasl->connection,
       
   861 							  sasl->failure_cb,
       
   862 							  LM_MESSAGE_TYPE_FAILURE);
       
   863 	}
       
   864 
       
   865 	g_free (sasl);
       
   866 }
       
   867 
       
   868 
       
   869 void
       
   870 lm_sasl_get_auth_params (LmSASL *sasl, const gchar **username,
       
   871 	const gchar **password)
       
   872 {
       
   873 	*username = sasl->username;
       
   874 	*password = sasl->password;
       
   875 }
       
   876