dovecot-2.2: lib-imap-client: Use lib-sasl to perform the AUTHEN...

dovecot at dovecot.org dovecot at dovecot.org
Thu Jan 8 20:54:11 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/ee135359faa4
changeset: 18141:ee135359faa4
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Jan 08 22:32:20 2015 +0200
description:
lib-imap-client: Use lib-sasl to perform the AUTHENTICATE command handling.

diffstat:

 src/lib-imap-client/Makefile.am        |    1 +
 src/lib-imap-client/imapc-connection.c |  142 +++++++++++++++++++++++++-------
 2 files changed, 111 insertions(+), 32 deletions(-)

diffs (230 lines):

diff -r 51e8bbc82edd -r ee135359faa4 src/lib-imap-client/Makefile.am
--- a/src/lib-imap-client/Makefile.am	Wed Jan 07 00:37:50 2015 +0200
+++ b/src/lib-imap-client/Makefile.am	Thu Jan 08 22:32:20 2015 +0200
@@ -3,6 +3,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-dns \
+	-I$(top_srcdir)/src/lib-sasl \
 	-I$(top_srcdir)/src/lib-ssl-iostream \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap
diff -r 51e8bbc82edd -r ee135359faa4 src/lib-imap-client/imapc-connection.c
--- a/src/lib-imap-client/imapc-connection.c	Wed Jan 07 00:37:50 2015 +0200
+++ b/src/lib-imap-client/imapc-connection.c	Thu Jan 08 22:32:20 2015 +0200
@@ -9,6 +9,7 @@
 #include "write-full.h"
 #include "str.h"
 #include "dns-lookup.h"
+#include "dsasl-client.h"
 #include "iostream-rawlog.h"
 #include "iostream-ssl.h"
 #include "imap-quote.h"
@@ -20,6 +21,7 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#define IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE INT_MAX
 #define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
 
 enum imapc_input_state {
@@ -53,6 +55,8 @@
 	imapc_command_callback_t *callback;
 	void *context;
 
+	/* This is the AUTHENTICATE command */
+	unsigned int authenticate:1;
 	/* This is the IDLE command */
 	unsigned int idle:1;
 	/* Waiting for '+' literal reply before we can continue */
@@ -82,6 +86,7 @@
 	struct timeout *to;
 	struct timeout *to_output;
 	struct dns_lookup *dns_lookup;
+	struct dsasl_client *sasl_client;
 
 	struct ssl_iostream *ssl_iostream;
 
@@ -680,11 +685,10 @@
 	imapc_connection_lfiles_free(conn);
 }
 
-static void imapc_connection_login_cb(const struct imapc_command_reply *reply,
-				      void *context)
+static void
+imapc_connection_auth_finish(struct imapc_connection *conn,
+			     const struct imapc_command_reply *reply)
 {
-	struct imapc_connection *conn = context;
-
 	if (reply->state != IMAPC_COMMAND_STATE_OK) {
 		if (conn->login_callback != NULL)
 			imapc_login_callback(conn, reply);
@@ -706,33 +710,62 @@
 	imapc_command_send_more(conn);
 }
 
-static const char *
-imapc_connection_get_sasl_plain_request(struct imapc_connection *conn)
+static void imapc_connection_login_cb(const struct imapc_command_reply *reply,
+				      void *context)
 {
-	const struct imapc_client_settings *set = &conn->client->set;
-	string_t *in, *out;
+	struct imapc_connection *conn = context;
 
-	in = t_str_new(128);
-	if (set->master_user != NULL) {
-		str_append(in, set->username);
-		str_append_c(in, '\0');
-		str_append(in, set->master_user);
+	imapc_connection_auth_finish(conn, reply);
+}
+
+static void
+imapc_connection_authenticate_cb(const struct imapc_command_reply *reply,
+				 void *context)
+{
+	struct imapc_connection *conn = context;
+	const unsigned char *sasl_output;
+	unsigned int sasl_output_len;
+	unsigned int input_len;
+	buffer_t *buf;
+	const char *error;
+
+	if (reply->state != IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE) {
+		dsasl_client_free(&conn->sasl_client);
+		imapc_connection_auth_finish(conn, reply);
+		return;
+	}
+
+	input_len = strlen(reply->text_full);
+	buf = buffer_create_dynamic(pool_datastack_create(),
+				    MAX_BASE64_DECODED_SIZE(input_len));
+	if (base64_decode(reply->text_full, input_len, NULL, buf) < 0) {
+		i_error("imapc(%s): Authentication failed: Server sent non-base64 input for AUTHENTICATE: %s",
+			conn->name, reply->text_full);
+	} else if (dsasl_client_input(conn->sasl_client, buf->data, buf->used, &error) < 0) {
+		i_error("imapc(%s): Authentication failed: %s",
+			conn->name, error);
+	} else if (dsasl_client_output(conn->sasl_client, &sasl_output,
+				       &sasl_output_len, &error) < 0) {
+		i_error("imapc(%s): Authentication failed: %s",
+			conn->name, error);
 	} else {
-		str_append_c(in, '\0');
-		str_append(in, set->username);
+		string_t *imap_output =
+			t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len)+2);
+		base64_encode(sasl_output, sasl_output_len, imap_output);
+		str_append(imap_output, "\r\n");
+		o_stream_nsend(conn->output, str_data(imap_output),
+			       str_len(imap_output));
+		return;
 	}
-	str_append_c(in, '\0');
-	str_append(in, set->password);
-
-	out = t_str_new(128);
-	base64_encode(in->data, in->used, out);
-	return str_c(out);
+	imapc_connection_disconnect(conn);
 }
 
 static void imapc_connection_authenticate(struct imapc_connection *conn)
 {
 	const struct imapc_client_settings *set = &conn->client->set;
 	struct imapc_command *cmd;
+	struct dsasl_client_settings sasl_set;
+	const struct dsasl_client_mech *sasl_mech;
 
 	if (conn->client->set.debug) {
 		if (set->master_user == NULL) {
@@ -744,22 +777,56 @@
 		}
 	}
 
-	cmd = imapc_connection_cmd(conn, imapc_connection_login_cb,
-				   conn);
-	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
-
 	if ((set->master_user == NULL &&
 	     !need_literal(set->username) && !need_literal(set->password)) ||
 	    (conn->capabilities & IMAPC_CAPABILITY_AUTH_PLAIN) == 0) {
 		/* We can use LOGIN command */
+		cmd = imapc_connection_cmd(conn, imapc_connection_login_cb,
+					   conn);
+		imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
 		imapc_command_sendf(cmd, "LOGIN %s %s",
 				    set->username, set->password);
-	} else if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) {
-		imapc_command_sendf(cmd, "AUTHENTICATE PLAIN %1s",
-			imapc_connection_get_sasl_plain_request(conn));
+		return;
+	}
+
+	memset(&sasl_set, 0, sizeof(sasl_set));
+	if (set->master_user == NULL)
+		sasl_set.authid = set->username;
+	else {
+		sasl_set.authid = set->master_user;
+		sasl_set.authzid = set->username;
+	}
+	sasl_set.password = set->password;
+
+	sasl_mech = &dsasl_client_mech_plain;
+	conn->sasl_client = dsasl_client_new(sasl_mech, &sasl_set);
+
+	cmd = imapc_connection_cmd(conn, imapc_connection_authenticate_cb, conn);
+	cmd->authenticate = TRUE;
+	imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
+
+	if ((conn->capabilities & IMAPC_CAPABILITY_SASL_IR) != 0) {
+		const unsigned char *sasl_output;
+		unsigned int sasl_output_len;
+		string_t *sasl_output_base64;
+		const char *error;
+
+		if (dsasl_client_output(conn->sasl_client, &sasl_output,
+					&sasl_output_len, &error) < 0) {
+			i_error("imapc(%s): Failed to create initial SASL reply: %s",
+				conn->name, error);
+			imapc_connection_disconnect(conn);
+			return;
+		}
+		sasl_output_base64 = t_str_new(MAX_BASE64_ENCODED_SIZE(sasl_output_len));
+		base64_encode(sasl_output, sasl_output_len, sasl_output_base64);
+
+		imapc_command_sendf(cmd, "AUTHENTICATE %1s %1s",
+				    dsasl_client_mech_get_name(sasl_mech),
+				    str_c(sasl_output_base64));
 	} else {
-		imapc_command_sendf(cmd, "AUTHENTICATE PLAIN\r\n%1s",
-			imapc_connection_get_sasl_plain_request(conn));
+		imapc_command_sendf(cmd, "AUTHENTICATE %1s",
+				    dsasl_client_mech_get_name(sasl_mech));
 	}
 }
 
@@ -960,8 +1027,19 @@
 		cmds[0]->wait_for_literal = FALSE;
 		imapc_command_send_more(conn);
 	} else {
-		imapc_connection_input_error(conn, "Unexpected '+': %s", line);
-		return -1;
+		cmds = array_get(&conn->cmd_wait_list, &cmds_count);
+		if (cmds_count > 0 && cmds[0]->authenticate) {
+			/* continue AUTHENTICATE */
+			struct imapc_command_reply reply;
+
+			memset(&reply, 0, sizeof(reply));
+			reply.state = IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE;
+			reply.text_full = line;
+			cmds[0]->callback(&reply, cmds[0]->context);
+		} else {
+			imapc_connection_input_error(conn, "Unexpected '+': %s", line);
+			return -1;
+		}
 	}
 
 	imapc_connection_input_reset(conn);


More information about the dovecot-cvs mailing list