changeset 28725:4f45aae3ace1

Let's try a more complex set of return states / values for auth mechs. This won't build with Cyrus support yet.
author Paul Aurich <paul@darkrain42.org>
date Tue, 01 Dec 2009 07:32:53 +0000
parents 554ae3e78952
children d558d141aaae
files libpurple/protocols/jabber/auth.c libpurple/protocols/jabber/auth.h libpurple/protocols/jabber/auth_digest_md5.c libpurple/protocols/jabber/auth_plain.c libpurple/protocols/jabber/auth_scram.c
diffstat 5 files changed, 104 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/auth.c	Tue Dec 01 04:21:40 2009 +0000
+++ b/libpurple/protocols/jabber/auth.c	Tue Dec 01 07:32:53 2009 +0000
@@ -162,8 +162,10 @@
 {
 	GSList *mechanisms = NULL;
 	GSList *l;
-	xmlnode *response;
+	xmlnode *response = NULL;
 	xmlnode *mechs, *mechnode;
+	JabberSaslState state;
+	const char *msg = NULL;
 
 	if(js->registration) {
 		jabber_register_start(js);
@@ -214,8 +216,12 @@
 		return;
 	}
 
-	response = js->auth_mech->start(js, mechs);
-	if (response) {
+	state = js->auth_mech->start(js, mechs, &response, &msg);
+	if (state == JABBER_SASL_STATE_FAIL) {
+		purple_connection_error_reason(js->gc,
+				PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+				msg ? msg : _("Unknown Error"));
+	} else if (response) {
 		jabber_send(js, response);
 		xmlnode_free(response);
 	}
@@ -413,8 +419,14 @@
 	}
 
 	if (js->auth_mech && js->auth_mech->handle_challenge) {
-		xmlnode *response = js->auth_mech->handle_challenge(js, packet);
-		if (response != NULL) {
+		xmlnode *response = NULL;
+		const char *msg = NULL;
+		JabberSaslState state = js->auth_mech->handle_challenge(js, packet, &response, &msg);
+		if (state == JABBER_SASL_STATE_FAIL) {
+			purple_connection_error_reason(js->gc,
+					PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+					msg ? msg : _("Invalid challenge from server"));
+		} else if (response) {
 			jabber_send(js, response);
 			xmlnode_free(response);
 		}
@@ -433,9 +445,21 @@
 		return;
 	}
 
-	if (js->auth_mech && js->auth_mech->handle_success &&
-			!js->auth_mech->handle_success(js, packet)) {
-		return;
+	if (js->auth_mech && js->auth_mech->handle_success) {
+		const char *msg = NULL;
+		JabberSaslState state = js->auth_mech->handle_success(js, packet, &msg);
+
+		if (state == JABBER_SASL_STATE_FAIL) {
+			purple_connection_error_reason(js->gc,
+					PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+					msg ? msg : _("Invalid response from server"));
+			return;
+		} else if (state == JABBER_SASL_STATE_CONTINUE) {
+			purple_connection_error_reason(js->gc,
+					PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE,
+					msg ? msg : _("Server thinks authentication is complete, but client does not"));
+			return;
+		}
 	}
 
 	/*
@@ -452,8 +476,11 @@
 	char *msg;
 
 	if (js->auth_mech && js->auth_mech->handle_failure) {
-		xmlnode *stanza = js->auth_mech->handle_failure(js, packet);
-		if (stanza) {
+		xmlnode *stanza = NULL;
+		const char *msg = NULL;
+		JabberSaslState state = js->auth_mech->handle_failure(js, packet, &stanza, &msg);
+
+		if (state != JABBER_SASL_STATE_FAIL && stanza) {
 			jabber_send(js, stanza);
 			xmlnode_free(stanza);
 			return;
--- a/libpurple/protocols/jabber/auth.h	Tue Dec 01 04:21:40 2009 +0000
+++ b/libpurple/protocols/jabber/auth.h	Tue Dec 01 07:32:53 2009 +0000
@@ -29,13 +29,19 @@
 #include "jabber.h"
 #include "xmlnode.h"
 
+typedef enum {
+	JABBER_SASL_STATE_FAIL = -1,    /* Abort, Retry, Fail? */
+	JABBER_SASL_STATE_OK = 0,       /* Hooray! */
+	JABBER_SASL_STATE_CONTINUE = 1  /* More authentication required */
+} JabberSaslState;
+
 struct _JabberSaslMech {
 	gint8 priority; /* Higher priority will be tried before lower priority */
 	const gchar *name;
-	xmlnode *(*start)(JabberStream *js, xmlnode *mechanisms);
-	xmlnode *(*handle_challenge)(JabberStream *js, xmlnode *packet);
-	gboolean (*handle_success)(JabberStream *js, xmlnode *packet);
-	xmlnode *(*handle_failure)(JabberStream *js, xmlnode *packet);
+	JabberSaslState (*start)(JabberStream *js, xmlnode *mechanisms, xmlnode **reply, const char **msg);
+	JabberSaslState (*handle_challenge)(JabberStream *js, xmlnode *packet, xmlnode **reply, const char **msg);
+	JabberSaslState (*handle_success)(JabberStream *js, xmlnode *packet, const char **msg);
+	JabberSaslState (*handle_failure)(JabberStream *js, xmlnode *packet, xmlnode **reply, const char **msg);
 	void (*dispose)(JabberStream *js);
 };
 
--- a/libpurple/protocols/jabber/auth_digest_md5.c	Tue Dec 01 04:21:40 2009 +0000
+++ b/libpurple/protocols/jabber/auth_digest_md5.c	Tue Dec 01 07:32:53 2009 +0000
@@ -30,15 +30,16 @@
 #include "auth.h"
 #include "jabber.h"
 
-static xmlnode *digest_md5_start(JabberStream *js, xmlnode *packet)
+static JabberSaslState
+digest_md5_start(JabberStream *js, xmlnode *packet, xmlnode **response,
+                 const char **msg)
 {
-	xmlnode *auth;
-
-	auth = xmlnode_new("auth");
+	xmlnode *auth = xmlnode_new("auth");
 	xmlnode_set_namespace(auth, NS_XMPP_SASL);
 	xmlnode_set_attrib(auth, "mechanism", "DIGEST-MD5");
 
-	return auth;
+	*response = auth;
+	return JABBER_SASL_STATE_CONTINUE;
 }
 
 /* Parts of this algorithm are inspired by stuff in libgsasl */
@@ -163,19 +164,20 @@
 	return z;
 }
 
-static xmlnode *digest_md5_handle_challenge(JabberStream *js, xmlnode *packet)
+static JabberSaslState
+digest_md5_handle_challenge(JabberStream *js, xmlnode *packet,
+                            xmlnode **response, const char **msg)
 {
 	xmlnode *reply = NULL;
 	char *enc_in = xmlnode_get_data(packet);
 	char *dec_in;
 	char *enc_out;
 	GHashTable *parts;
+	JabberSaslState state = JABBER_SASL_STATE_CONTINUE;
 
 	if (!enc_in) {
-		purple_connection_error_reason(js->gc,
-			PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-			_("Invalid response from server"));
-		return NULL;
+		*msg = _("Invalid response from server");
+		return JABBER_SASL_STATE_FAIL;
 	}
 
 	dec_in = (char *)purple_base64_decode(enc_in, NULL);
@@ -191,9 +193,8 @@
 			reply = xmlnode_new("response");
 			xmlnode_set_namespace(reply, NS_XMPP_SASL);
 		} else {
-			purple_connection_error_reason(js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Invalid challenge from server"));
+			*msg = _("Invalid challenge from server");
+			state = JABBER_SASL_STATE_FAIL;
 		}
 		g_free(js->expected_rspauth);
 		js->expected_rspauth = NULL;
@@ -216,11 +217,10 @@
 		if(!realm)
 			realm = js->user->domain;
 
-		if (nonce == NULL || realm == NULL)
-			purple_connection_error_reason(js->gc,
-				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-				_("Invalid challenge from server"));
-		else {
+		if (nonce == NULL || realm == NULL) {
+			*msg = _("Invalid challenge from server");
+			state = JABBER_SASL_STATE_FAIL;
+		} else {
 			GString *response = g_string_new("");
 			char *a2;
 			char *auth_resp;
@@ -272,7 +272,8 @@
 	g_free(dec_in);
 	g_hash_table_destroy(parts);
 
-	return reply;
+	*response = reply;
+	return state;
 }
 
 static JabberSaslMech digest_md5_mech = {
--- a/libpurple/protocols/jabber/auth_plain.c	Tue Dec 01 04:21:40 2009 +0000
+++ b/libpurple/protocols/jabber/auth_plain.c	Tue Dec 01 07:32:53 2009 +0000
@@ -80,13 +80,16 @@
 		_("Server requires plaintext authentication over an unencrypted stream"));
 }
 
-static xmlnode *jabber_plain_start(JabberStream *js, xmlnode *packet)
+static JabberSaslState
+jabber_plain_start(JabberStream *js, xmlnode *packet, xmlnode **response, const char **error)
 {
 	PurpleAccount *account = purple_connection_get_account(js->gc);
 	char *msg;
 
-	if (jabber_stream_is_ssl(js) || purple_account_get_bool(account, "auth_plain_in_clear", FALSE))
-		return finish_plaintext_authentication(js);
+	if (jabber_stream_is_ssl(js) || purple_account_get_bool(account, "auth_plain_in_clear", FALSE)) {
+		*response = finish_plaintext_authentication(js);
+		return JABBER_SASL_STATE_OK;
+	}
 
 	msg = g_strdup_printf(_("%s requires plaintext authentication over an unencrypted connection.  Allow this and continue authentication?"),
 			purple_account_get_username(account));
@@ -97,7 +100,7 @@
 			account, NULL, NULL,
 			account, allow_plaintext_auth, disallow_plaintext_auth);
 	g_free(msg);
-	return NULL;
+	return JABBER_SASL_STATE_CONTINUE;
 }
 
 static JabberSaslMech plain_mech = {
--- a/libpurple/protocols/jabber/auth_scram.c	Tue Dec 01 04:21:40 2009 +0000
+++ b/libpurple/protocols/jabber/auth_scram.c	Tue Dec 01 07:32:53 2009 +0000
@@ -341,7 +341,8 @@
 	return tmp2;
 }
 
-static xmlnode *scram_start(JabberStream *js, xmlnode *mechanisms)
+static JabberSaslState
+scram_start(JabberStream *js, xmlnode *mechanisms, xmlnode **out, const char **error)
 {
 	xmlnode *reply;
 	JabberScramData *data;
@@ -355,8 +356,8 @@
 
 	prepped_node = jabber_saslprep(js->user->node);
 	if (!prepped_node) {
-		/* TODO: Error handling in the response value from scram_start */
-		return NULL;
+		*error = _("Unable to canonicalize username");
+		return JABBER_SASL_STATE_FAIL;
 	}
 
 	tmp = escape_username(prepped_node);
@@ -366,7 +367,8 @@
 	prepped_pass = jabber_saslprep(purple_connection_get_password(js->gc));
 	if (!prepped_pass) {
 		g_free(prepped_node);
-		return NULL;
+		*error = _("Unable to canonicalize password");
+		return JABBER_SASL_STATE_FAIL;
 	}
 
 	data = js->auth_mech_data = g_new0(JabberScramData, 1);
@@ -401,22 +403,26 @@
 	g_free(enc_out);
 	g_free(dec_out);
 
-	return reply;
+	*out = reply;
+	return JABBER_SASL_STATE_CONTINUE;
 }
 
-static xmlnode *scram_handle_challenge(JabberStream *js, xmlnode *challenge)
+static JabberSaslState
+scram_handle_challenge(JabberStream *js, xmlnode *challenge, xmlnode **out, const char **error)
 {
 	JabberScramData *data = js->auth_mech_data;
 	xmlnode *reply;
 	gchar *enc_in, *dec_in;
 	gchar *enc_out = NULL, *dec_out = NULL;
 	gsize len;
+	JabberSaslState state = JABBER_SASL_STATE_FAIL;
 
 	enc_in = xmlnode_get_data(challenge);
 	if (!enc_in || *enc_in == '\0') {
 		reply = xmlnode_new("abort");
 		xmlnode_set_namespace(reply, NS_XMPP_SASL);
 		data->step = -1;
+		*error = _("Invalid challenge from server");
 		goto out;
 	}
 
@@ -427,6 +433,7 @@
 		reply = xmlnode_new("abort");
 		xmlnode_set_namespace(reply, NS_XMPP_SASL);
 		data->step = -1;
+		*error = _("Malicious challenge from server");
 		goto out;
 	}
 
@@ -436,6 +443,7 @@
 		reply = xmlnode_new("abort");
 		xmlnode_set_namespace(reply, NS_XMPP_SASL);
 		data->step = -1;
+		*error = _("Invalid challenge from server");
 		goto out;
 	}
 
@@ -450,14 +458,18 @@
 		xmlnode_insert_data(reply, enc_out, -1);
 	}
 
+	state = JABBER_SASL_STATE_CONTINUE;
+
 out:
 	g_free(enc_out);
 	g_free(dec_out);
 
-	return reply;
+	*out = reply;
+	return state;
 }
 
-static gboolean scram_handle_success(JabberStream *js, xmlnode *packet)
+static JabberSaslState
+scram_handle_success(JabberStream *js, xmlnode *packet, const char **error)
 {
 	JabberScramData *data = js->auth_mech_data;
 	char *enc_in, *dec_in;
@@ -468,28 +480,32 @@
 	g_return_val_if_fail(enc_in != NULL && *enc_in != '\0', FALSE);
 
 	if (data->step == 3)
-		return TRUE;
+		return JABBER_SASL_STATE_OK;
 
-	if (data->step != 2)
-		return FALSE;
+	if (data->step != 2) {
+		*error = _("Unexpected response from server");
+		return JABBER_SASL_STATE_FAIL;
+	}
 
 	dec_in = (gchar *)purple_base64_decode(enc_in, &len);
 	g_free(enc_in);
 	if (!dec_in || len != strlen(dec_in)) {
 		/* Danger afoot; SCRAM shouldn't contain NUL bytes */
 		g_free(dec_in);
-		return FALSE;
+		*error = _("Invalid challenge from server");
+		return JABBER_SASL_STATE_FAIL;
 	}
 
 	purple_debug_misc("jabber", "decoded success: %s\n", dec_in);
 
 	if (!jabber_scram_feed_parser(data, dec_in, &dec_out) || dec_out != NULL) {
 		g_free(dec_out);
-		return FALSE;
+		*error = _("Invalid challenge from server");
+		return JABBER_SASL_STATE_FAIL;
 	}
 
 	/* Hooray */
-	return TRUE;
+	return JABBER_SASL_STATE_OK;
 }
 
 void jabber_scram_data_destroy(JabberScramData *data)