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