dovecot-2.1: imap-login: Handle SASL-IR without overflowing mast...

dovecot at dovecot.org dovecot at dovecot.org
Thu Jan 19 15:57:29 EET 2012


details:   http://hg.dovecot.org/dovecot-2.1/rev/b86f7dd170c6
changeset: 13953:b86f7dd170c6
user:      Timo Sirainen <tss at iki.fi>
date:      Thu Jan 19 15:53:42 2012 +0200
description:
imap-login: Handle SASL-IR without overflowing master_auth_request's buffer.

diffstat:

 src/imap-login/client-authenticate.c  |  45 ++++++++++++++-----
 src/imap-login/client-authenticate.h  |   2 +-
 src/imap-login/client.c               |  79 +++++++++++++++++++++-------------
 src/imap-login/client.h               |   3 +
 src/lib-master/master-auth.h          |   5 +-
 src/login-common/client-common-auth.c |  10 ++--
 src/login-common/client-common.h      |  10 +++-
 7 files changed, 99 insertions(+), 55 deletions(-)

diffs (286 lines):

diff -r 141d1e26b13a -r b86f7dd170c6 src/imap-login/client-authenticate.c
--- a/src/imap-login/client-authenticate.c	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/imap-login/client-authenticate.c	Thu Jan 19 15:53:42 2012 +0200
@@ -118,22 +118,43 @@
 	return client_auth_begin(&imap_client->common, mech_name, init_resp);
 }
 
-int cmd_authenticate(struct imap_client *imap_client,
-		     const struct imap_arg *args)
+int cmd_authenticate(struct imap_client *imap_client)
 {
-	const char *mech_name, *init_resp;
+	/* NOTE: This command's input is handled specially because the
+	   SASL-IR can be large. */
+	struct client *client = &imap_client->common;
+	const unsigned char *data;
+	size_t i, size;
+	int ret;
 
 	/* <auth mechanism name> [<initial SASL response>] */
-	if (!imap_arg_get_atom(&args[0], &mech_name) || *mech_name == '\0')
-		return -1;
-	if (imap_arg_get_atom(&args[1], &init_resp))
-		args++;
-	else
-		init_resp = NULL;
-	if (!IMAP_ARG_IS_EOL(&args[1]))
-		return -1;
+	if (client->auth_mech_name == NULL) {
+		data = i_stream_get_data(client->input, &size);
+		for (i = 0; i < size; i++) {
+			if (data[i] == ' ' ||
+			    data[i] == '\r' || data[i] == '\n')
+				break;
+		}
+		if (i == size)
+			return 0;
+		if (i == 0) {
+			/* empty mechanism name */
+			imap_client->skip_line = TRUE;
+			return -1;
+		}
+		client->auth_mech_name = i_strndup(data, i);
+		if (data[i] == ' ')
+			i++;
+		i_stream_skip(client->input, i);
+	}
 
-	return imap_client_auth_begin(imap_client, mech_name, init_resp);
+	/* get SASL-IR, if any */
+	if ((ret = client_auth_read_line(client)) <= 0)
+		return ret;
+
+	return imap_client_auth_begin(imap_client,
+				      t_strdup(client->auth_mech_name),
+				      str_c(client->auth_response));
 }
 
 int cmd_login(struct imap_client *imap_client, const struct imap_arg *args)
diff -r 141d1e26b13a -r b86f7dd170c6 src/imap-login/client-authenticate.h
--- a/src/imap-login/client-authenticate.h	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/imap-login/client-authenticate.h	Thu Jan 19 15:53:42 2012 +0200
@@ -9,6 +9,6 @@
 				   const struct client_auth_reply *reply);
 
 int cmd_login(struct imap_client *client, const struct imap_arg *args);
-int cmd_authenticate(struct imap_client *client, const struct imap_arg *args);
+int cmd_authenticate(struct imap_client *client);
 
 #endif
diff -r 141d1e26b13a -r b86f7dd170c6 src/imap-login/client.c
--- a/src/imap-login/client.c	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/imap-login/client.c	Thu Jan 19 15:53:42 2012 +0200
@@ -169,8 +169,6 @@
 	cmd = t_str_ucase(cmd);
 	if (strcmp(cmd, "LOGIN") == 0)
 		return cmd_login(client, args);
-	if (strcmp(cmd, "AUTHENTICATE") == 0)
-		return cmd_authenticate(client, args);
 	if (strcmp(cmd, "CAPABILITY") == 0)
 		return cmd_capability(client);
 	if (strcmp(cmd, "STARTTLS") == 0)
@@ -214,12 +212,43 @@
 	return TRUE;
 }
 
+static int client_parse_command(struct imap_client *client,
+				const struct imap_arg **args_r)
+{
+	const char *msg;
+	bool fatal;
+
+	switch (imap_parser_read_args(client->parser, 0, 0, args_r)) {
+	case -1:
+		/* error */
+		msg = imap_parser_get_error(client->parser, &fatal);
+		if (fatal) {
+			client_send_line(&client->common,
+					 CLIENT_CMD_REPLY_BYE, msg);
+			client_destroy(&client->common,
+				t_strconcat("Disconnected: ", msg, NULL));
+			return FALSE;
+		}
+
+		client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, msg);
+		client->cmd_finished = TRUE;
+		client->skip_line = TRUE;
+		return -1;
+	case -2:
+		/* not enough data */
+		return 0;
+	default:
+		/* we read the entire line - skip over the CRLF */
+		if (!client_skip_line(client))
+			i_unreached();
+		return 1;
+	}
+}
+
 static bool client_handle_input(struct imap_client *client)
 {
 	const struct imap_arg *args;
-	const char *msg;
 	int ret;
-	bool fatal;
 
 	i_assert(!client->common.authenticating);
 
@@ -245,7 +274,8 @@
                 client->cmd_tag = imap_parser_read_word(client->parser);
 		if (client->cmd_tag == NULL)
 			return FALSE; /* need more data */
-		if (!imap_is_valid_tag(client->cmd_tag)) {
+		if (!imap_is_valid_tag(client->cmd_tag) ||
+		    strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) {
 			/* the tag is invalid, don't allow it and don't
 			   send it back. this attempts to prevent any
 			   potentially dangerous replies in case someone tries
@@ -260,34 +290,21 @@
 			return FALSE; /* need more data */
 	}
 
-	switch (imap_parser_read_args(client->parser, 0, 0, &args)) {
-	case -1:
-		/* error */
-		msg = imap_parser_get_error(client->parser, &fatal);
-		if (fatal) {
-			client_send_line(&client->common,
-					 CLIENT_CMD_REPLY_BYE, msg);
-			client_destroy(&client->common,
-				t_strconcat("Disconnected: ", msg, NULL));
+	if (strcasecmp(client->cmd_name, "AUTHENTICATE") == 0) {
+		/* SASL-IR may need more space than input buffer's size,
+		   so we'll handle it as a special case. */
+		ret = cmd_authenticate(client);
+		if (ret == 0)
 			return FALSE;
-		}
-
-		client_send_line(&client->common, CLIENT_CMD_REPLY_BAD, msg);
-		client->cmd_finished = TRUE;
-		client->skip_line = TRUE;
-		return TRUE;
-	case -2:
-		/* not enough data */
-		return FALSE;
+	} else {
+		ret = client_parse_command(client, &args);
+		if (ret < 0)
+			return TRUE;
+		if (ret == 0)
+			return FALSE;
+		ret = *client->cmd_tag == '\0' ? -1 :
+			client_command_execute(client, client->cmd_name, args);
 	}
-	/* we read the entire line - skip over the CRLF */
-	if (!client_skip_line(client))
-		i_unreached();
-
-	if (*client->cmd_tag == '\0')
-		ret = -1;
-	else
-		ret = client_command_execute(client, client->cmd_name, args);
 
 	client->cmd_finished = TRUE;
 	if (ret == -2 && strcasecmp(client->cmd_tag, "LOGIN") == 0) {
diff -r 141d1e26b13a -r b86f7dd170c6 src/imap-login/client.h
--- a/src/imap-login/client.h	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/imap-login/client.h	Thu Jan 19 15:53:42 2012 +0200
@@ -4,6 +4,9 @@
 #include "network.h"
 #include "client-common.h"
 
+/* Master prefix is: <1|0><imap tag><NUL> */
+#define IMAP_TAG_MAX_LEN (LOGIN_MAX_MASTER_PREFIX_LEN-2)
+
 struct imap_client {
 	struct client common;
 
diff -r 141d1e26b13a -r b86f7dd170c6 src/lib-master/master-auth.h
--- a/src/lib-master/master-auth.h	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/lib-master/master-auth.h	Thu Jan 19 15:53:42 2012 +0200
@@ -13,9 +13,8 @@
 /* Authentication client process's cookie size */
 #define MASTER_AUTH_COOKIE_SIZE (128/8)
 
-/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
-   to make sure there's space to transfer the command tag  */
-#define MASTER_AUTH_MAX_DATA_SIZE (1024*2)
+/* LOGIN_MAX_INBUF_SIZE should be based on this.*/
+#define MASTER_AUTH_MAX_DATA_SIZE 1024
 
 #define MASTER_AUTH_ERRMSG_INTERNAL_FAILURE \
 	"Internal error occurred. Refer to server log for more information."
diff -r 141d1e26b13a -r b86f7dd170c6 src/login-common/client-common-auth.c
--- a/src/login-common/client-common-auth.c	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/login-common/client-common-auth.c	Thu Jan 19 15:53:42 2012 +0200
@@ -335,7 +335,7 @@
 	return client->v.auth_handle_reply(client, reply);
 }
 
-static int client_auth_read_line(struct client *client)
+int client_auth_read_line(struct client *client)
 {
 	const unsigned char *data;
 	size_t i, size;
@@ -351,6 +351,8 @@
 		if (data[i] == '\n')
 			break;
 	}
+	if (client->auth_response == NULL)
+		client->auth_response = str_new(default_pool, I_MAX(i+1, 256));
 	if (str_len(client->auth_response) + i > CLIENT_AUTH_BUF_MAX_SIZE) {
 		client_destroy(client, "Authentication response too large");
 		return -1;
@@ -480,7 +482,8 @@
 		if (client->to_auth_waiting != NULL)
 			timeout_remove(&client->to_auth_waiting);
 
-		str_truncate(client->auth_response, 0);
+		if (client->auth_response != NULL)
+			str_truncate(client->auth_response, 0);
 
 		i_assert(client->io == NULL);
 		client->auth_waiting = TRUE;
@@ -508,9 +511,6 @@
 	}
 
 
-	if (client->auth_response == NULL)
-		client->auth_response = str_new(default_pool, 256);
-
 	client_ref(client);
 	client->auth_initializing = TRUE;
 	sasl_server_auth_begin(client, login_binary->protocol, mech_name,
diff -r 141d1e26b13a -r b86f7dd170c6 src/login-common/client-common.h
--- a/src/login-common/client-common.h	Thu Jan 19 15:41:16 2012 +0200
+++ b/src/login-common/client-common.h	Thu Jan 19 15:53:42 2012 +0200
@@ -5,13 +5,16 @@
 #include "login-proxy.h"
 #include "sasl-server.h"
 
+#define LOGIN_MAX_MASTER_PREFIX_LEN 128
+
 /* max. size of input buffer. this means:
 
-   IMAP: Max. length of a single parameter. SASL initial response can be
-         long with GSSAPI.
+   IMAP: Max. length of command's all parameters. SASL-IR is read into
+         a separate larger buffer.
    POP3: Max. length of a command line (spec says 512 would be enough)
 */
-#define LOGIN_MAX_INBUF_SIZE 4096
+#define LOGIN_MAX_INBUF_SIZE \
+	(MASTER_AUTH_MAX_DATA_SIZE - LOGIN_MAX_MASTER_PREFIX_LEN)
 /* max. size of output buffer. if it gets full, the client is disconnected.
    SASL authentication gives the largest output. */
 #define LOGIN_MAX_OUTBUF_SIZE 4096
@@ -171,6 +174,7 @@
 int client_auth_begin(struct client *client, const char *mech_name,
 		      const char *init_resp);
 bool client_check_plaintext_auth(struct client *client, bool pass_sent);
+int client_auth_read_line(struct client *client);
 
 void client_proxy_finish_destroy_client(struct client *client);
 void client_proxy_log_failure(struct client *client, const char *line);


More information about the dovecot-cvs mailing list