import dovecot-2.3.8-9.el8

This commit is contained in:
CentOS Sources 2021-05-18 02:44:45 -04:00 committed by Andrew Lukoshko
parent 5e1a55119d
commit 5f30a6228e
20 changed files with 1592 additions and 3 deletions

View File

@ -0,0 +1,26 @@
From 68165c8acc6d32a06f8dce2ef515c714c243ce4e Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 18:33:20 +0300
Subject: [PATCH] imap: Escape tag when sending it to imap-hibernate process
---
src/imap/imap-client-hibernate.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/imap/imap-client-hibernate.c b/src/imap/imap-client-hibernate.c
index f639d722cb..4ef323453c 100644
--- a/src/imap/imap-client-hibernate.c
+++ b/src/imap/imap-client-hibernate.c
@@ -97,8 +97,10 @@ static void imap_hibernate_write_cmd(struct client *client, string_t *cmd,
str_printfa(cmd, "\tuid=%s", dec2str(user->uid));
if (user->gid != (gid_t)-1)
str_printfa(cmd, "\tgid=%s", dec2str(user->gid));
- if (tag != NULL)
- str_printfa(cmd, "\ttag=%s", tag);
+ if (tag != NULL) {
+ str_append(cmd, "\ttag=");
+ str_append_tabescaped(cmd, tag);
+ }
str_append(cmd, "\tstats=");
str_append_tabescaped(cmd, client_stats(client));
if (client->command_queue != NULL &&

View File

@ -0,0 +1,25 @@
From 73937b5fe7eb1dde76f30ef6b181c920bbbc4558 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 24 Aug 2020 16:58:16 +0300
Subject: [PATCH] imap: Fix crash if imap-hibernate socket can't be connected
to
The error was supposed to be returned to caller, not logged directly.
---
src/imap/imap-client-hibernate.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/imap/imap-client-hibernate.c b/src/imap/imap-client-hibernate.c
index 4ef323453c..d3451b1bf6 100644
--- a/src/imap/imap-client-hibernate.c
+++ b/src/imap/imap-client-hibernate.c
@@ -176,7 +176,8 @@ imap_hibernate_process_send(struct client *client, const buffer_t *state,
"/"IMAP_HIBERNATE_SOCKET_NAME, NULL);
fd = net_connect_unix_with_retries(path, 1000);
if (fd == -1) {
- i_error("net_connect_unix(%s) failed: %m", path);
+ *error_r = t_strdup_printf(
+ "net_connect_unix(%s) failed: %m", path);
return -1;
}
net_set_nonblock(fd, FALSE);

View File

@ -0,0 +1,31 @@
From c7d158681fabdb3044bd213c332e489b46625a3b Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 24 Aug 2020 19:10:10 +0300
Subject: [PATCH] imap: Delay initializing client IO until
client_create_finish()
This helps writing unit tests.
---
src/imap/imap-client.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c
index 95e57dbf53..c8ffeafc43 100644
--- a/src/imap/imap-client.c
+++ b/src/imap/imap-client.c
@@ -143,7 +143,6 @@ struct client *client_create(int fd_in, int fd_out,
o_stream_set_flush_callback(client->output, client_output, client);
p_array_init(&client->module_contexts, client->pool, 5);
- client->io = io_add_istream(client->input, client_input, client);
client->last_input = ioloop_time;
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
@@ -228,6 +227,7 @@ int client_create_finish(struct client *client, const char **error_r)
return -1;
mail_namespaces_set_storage_callbacks(client->user->namespaces,
&mail_storage_callbacks, client);
+ client->io = io_add_istream(client->input, client_input, client);
client->v.init(client);
return 0;

View File

@ -0,0 +1,105 @@
From 1a27cfa8e337b7e3298ba230059e766cdbc1123d Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 24 Aug 2020 19:10:43 +0300
Subject: [PATCH] imap: imap_client_hibernate() - Return reason string on
failure
This helps writing a unit test for it.
---
src/imap/cmd-idle.c | 3 ++-
src/imap/imap-client-hibernate.c | 10 +++++++++-
src/imap/imap-client.h | 5 +++--
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/src/imap/cmd-idle.c b/src/imap/cmd-idle.c
index 8a05582d03..2b31dc714e 100644
--- a/src/imap/cmd-idle.c
+++ b/src/imap/cmd-idle.c
@@ -175,11 +175,12 @@ static void idle_add_keepalive_timeout(struct cmd_idle_context *ctx)
static void idle_hibernate_timeout(struct cmd_idle_context *ctx)
{
struct client *client = ctx->client;
+ const char *reason;
i_assert(ctx->sync_ctx == NULL);
i_assert(!ctx->sync_pending);
- if (imap_client_hibernate(&client)) {
+ if (imap_client_hibernate(&client, &reason)) {
/* client may be destroyed now */
} else {
/* failed - don't bother retrying */
diff --git a/src/imap/imap-client-hibernate.c b/src/imap/imap-client-hibernate.c
index d3451b1bf6..0709e4a244 100644
--- a/src/imap/imap-client-hibernate.c
+++ b/src/imap/imap-client-hibernate.c
@@ -203,19 +203,23 @@ imap_hibernate_process_send(struct client *client, const buffer_t *state,
return 0;
}
-bool imap_client_hibernate(struct client **_client)
+bool imap_client_hibernate(struct client **_client, const char **reason_r)
{
struct client *client = *_client;
buffer_t *state;
const char *error;
int ret, fd_notify = -1, fd_hibernate = -1;
+ *reason_r = NULL;
+
if (client->fd_in != client->fd_out) {
/* we won't try to hibernate stdio clients */
+ *reason_r = "stdio clients can't be hibernated";
return FALSE;
}
if (o_stream_get_buffer_used_size(client->output) > 0) {
/* wait until we've sent the pending output to client */
+ *reason_r = "output pending to client";
return FALSE;
}
@@ -233,11 +237,13 @@ bool imap_client_hibernate(struct client **_client)
"Couldn't export state: %s (mailbox=%s)", error,
client->mailbox == NULL ? "" :
mailbox_get_vname(client->mailbox));
+ *reason_r = error;
} else if (ret == 0) {
e_debug(client->event, "Couldn't hibernate imap client: "
"Couldn't export state: %s (mailbox=%s)", error,
client->mailbox == NULL ? "" :
mailbox_get_vname(client->mailbox));
+ *reason_r = error;
}
if (ret > 0 && client->mailbox != NULL) {
fd_notify = mailbox_watch_extract_notify_fd(client->mailbox,
@@ -248,6 +254,7 @@ bool imap_client_hibernate(struct client **_client)
e_debug(client->event, "Couldn't hibernate imap client: "
"Couldn't extract notifications fd: %s",
error);
+ *reason_r = error;
ret = -1;
}
}
@@ -257,5 +264,6 @@ bool imap_client_hibernate(struct client **_client)
e_error(client->event,
"Couldn't hibernate imap client: %s", error);
+ *reason_r = error;
ret = -1;
}
}
diff --git a/src/imap/imap-client.h b/src/imap/imap-client.h
index f2ffe0d7c9..4e591d5c7c 100644
--- a/src/imap/imap-client.h
+++ b/src/imap/imap-client.h
@@ -323,8 +323,9 @@ enum mailbox_feature client_enabled_mailbox_features(struct client *client);
const char *const *client_enabled_features(struct client *client);
/* Send client processing to imap-idle process. If successful, returns TRUE
- and destroys the client. */
-bool imap_client_hibernate(struct client **client);
+ and destroys the client. If hibernation failed, the exact reason is
+ returned (mainly for unit tests). */
+bool imap_client_hibernate(struct client **client, const char **reason_r);
struct imap_search_update *
client_search_update_lookup(struct client *client, const char *tag,

View File

@ -0,0 +1,129 @@
diff -up dovecot-2.3.8/src/imap/imap-client-hibernate.c.CVE_2020_24386-prereq1 dovecot-2.3.8/src/imap/imap-client-hibernate.c
--- dovecot-2.3.8/src/imap/imap-client-hibernate.c.CVE_2020_24386-prereq1 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/imap/imap-client-hibernate.c 2021-01-08 17:14:40.051174282 +0100
@@ -19,24 +19,26 @@
#define IMAP_HIBERNATE_SEND_TIMEOUT_SECS 10
#define IMAP_HIBERNATE_HANDSHAKE "VERSION\timap-hibernate\t1\t0\n"
-static int imap_hibernate_handshake(int fd, const char *path)
+static int
+imap_hibernate_handshake(int fd, const char *path, const char **error_r)
{
char buf[1024];
ssize_t ret;
if (write_full(fd, IMAP_HIBERNATE_HANDSHAKE,
strlen(IMAP_HIBERNATE_HANDSHAKE)) < 0) {
- i_error("write(%s) failed: %m", path);
+ *error_r = t_strdup_printf("write(%s) failed: %m", path);
return -1;
} else if ((ret = read(fd, buf, sizeof(buf)-1)) < 0) {
- i_error("read(%s) failed: %m", path);
+ *error_r = t_strdup_printf("read(%s) failed: %m", path);
return -1;
} else if (ret > 0 && buf[ret-1] == '\n') {
buf[ret-1] = '\0';
if (version_string_verify(buf, "imap-hibernate", 1))
return 0;
}
- i_error("%s sent invalid VERSION handshake: %s", path, buf);
+ *error_r = t_strdup_printf("%s sent invalid VERSION handshake: %s",
+ path, buf);
return -1;
}
@@ -105,40 +107,42 @@ static void imap_hibernate_write_cmd(str
static int
imap_hibernate_process_send_cmd(int fd_socket, const char *path,
- const string_t *cmd, int fd_client)
+ const string_t *cmd, int fd_client,
+ const char **error_r)
{
ssize_t ret;
i_assert(fd_socket != -1);
i_assert(str_len(cmd) > 1);
- if (imap_hibernate_handshake(fd_socket, path) < 0)
+ if (imap_hibernate_handshake(fd_socket, path, error_r) < 0)
return -1;
if ((ret = fd_send(fd_socket, fd_client, str_data(cmd), 1)) < 0) {
- i_error("fd_send(%s) failed: %m", path);
+ *error_r = t_strdup_printf("fd_send(%s) failed: %m", path);
return -1;
}
if ((ret = write_full(fd_socket, str_data(cmd)+1, str_len(cmd)-1)) < 0) {
- i_error("write(%s) failed: %m", path);
+ *error_r = t_strdup_printf("write(%s) failed: %m", path);
return -1;
}
return 0;
}
-static int imap_hibernate_process_read(int fd, const char *path)
+static int
+imap_hibernate_process_read(int fd, const char *path, const char **error_r)
{
char buf[1024];
ssize_t ret;
if ((ret = read(fd, buf, sizeof(buf)-1)) < 0) {
- i_error("read(%s) failed: %m", path);
+ *error_r = t_strdup_printf("read(%s) failed: %m", path);
return -1;
} else if (ret == 0) {
- i_error("%s disconnected", path);
+ *error_r = t_strdup_printf("%s disconnected", path);
return -1;
} else if (buf[0] != '+') {
buf[ret] = '\0';
- i_error("%s returned failure: %s", path,
+ *error_r = t_strdup_printf("%s returned failure: %s", path,
ret > 0 && buf[0] == '-' ? buf+1 : buf);
return -1;
} else {
@@ -147,8 +151,8 @@ static int imap_hibernate_process_read(i
}
static int
-imap_hibernate_process_send(struct client *client,
- const buffer_t *state, int fd_notify, int *fd_r)
+imap_hibernate_process_send(struct client *client, const buffer_t *state,
+ int fd_notify, int *fd_r, const char **error_r)
{
string_t *cmd = t_str_new(512);
const char *path;
@@ -171,14 +175,14 @@ imap_hibernate_process_send(struct clien
imap_hibernate_write_cmd(client, cmd, state, fd_notify);
alarm(IMAP_HIBERNATE_SEND_TIMEOUT_SECS);
- if (imap_hibernate_process_send_cmd(fd, path, cmd, client->fd_in) < 0 ||
- imap_hibernate_process_read(fd, path) < 0)
+ if (imap_hibernate_process_send_cmd(fd, path, cmd, client->fd_in, error_r) < 0 ||
+ imap_hibernate_process_read(fd, path, error_r) < 0)
ret = -1;
else if (fd_notify != -1) {
if ((ret = fd_send(fd, fd_notify, "\n", 1)) < 0)
- i_error("fd_send(%s) failed: %m", path);
+ *error_r = t_strdup_printf("fd_send(%s) failed: %m", path);
else
- ret = imap_hibernate_process_read(fd, path);
+ ret = imap_hibernate_process_read(fd, path, error_r);
}
alarm(0);
if (ret < 0) {
@@ -229,8 +233,12 @@ bool imap_client_hibernate(struct client
}
}
if (ret > 0) {
- if (imap_hibernate_process_send(client, state, fd_notify, &fd_hibernate) < 0)
+ if (imap_hibernate_process_send(client, state, fd_notify,
+ &fd_hibernate, &error) < 0) {
+ e_error(client->event,
+ "Couldn't hibernate imap client: %s", error);
ret = -1;
+ }
}
i_close_fd(&fd_notify);
if (ret > 0) {

View File

@ -0,0 +1,185 @@
From b9a2f18466a0d3377bab3e7a57691bdd75d8507c Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 17:32:11 +0300
Subject: [PATCH] lib-imap: Add imap_parser_read_tag() and _read_command_name()
---
src/lib-imap/imap-parser.c | 67 +++++++++++++++++++++++++++++++++
src/lib-imap/imap-parser.h | 7 ++++
src/lib-imap/test-imap-parser.c | 67 +++++++++++++++++++++++++++++++++
3 files changed, 141 insertions(+)
diff --git a/src/lib-imap/imap-parser.c b/src/lib-imap/imap-parser.c
index b6c6e63fb1..52d79282fa 100644
--- a/src/lib-imap/imap-parser.c
+++ b/src/lib-imap/imap-parser.c
@@ -947,3 +947,70 @@ const char *imap_parser_read_word(struct imap_parser *parser)
return NULL;
}
}
+
+static int
+imap_parser_read_next_atom(struct imap_parser *parser, bool parsing_tag,
+ const char **atom_r)
+{
+ const unsigned char *data;
+ size_t i, data_size;
+
+ data = i_stream_get_data(parser->input, &data_size);
+
+ /*
+ tag = 1*<any ASTRING-CHAR except "+">
+ ASTRING-CHAR = ATOM-CHAR / resp-specials
+ ATOM-CHAR = <any CHAR except atom-specials>
+
+ x-command = "X" atom <experimental command arguments>
+ atom = 1*ATOM-CHAR
+ */
+ for (i = 0; i < data_size; i++) {
+ /* explicitly check for atom-specials, because
+ IS_ATOM_PARSER_INPUT() allows some atom-specials */
+ switch (data[i]) {
+ case ' ':
+ case '\r':
+ case '\n':
+ data_size = i + (data[i] == ' ' ? 1 : 0);
+ parser->line_size += data_size;
+ i_stream_skip(parser->input, data_size);
+ *atom_r = p_strndup(parser->pool, data, i);
+ /* don't allow empty string */
+ return i == 0 ? -1 : 1;
+ /* atom-specials: */
+ case '(':
+ case ')':
+ case '{':
+ /* list-wildcards: */
+ case '%':
+ case '*':
+ /* quoted-specials: */
+ case '"':
+ case '\\':
+ /* resp-specials: */
+ case ']':
+ return -1;
+ case '+':
+ if (parsing_tag)
+ return -1;
+ break;
+ default:
+ if ((unsigned char)data[i] < ' ' ||
+ (unsigned char)data[i] >= 0x80)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r)
+{
+ return imap_parser_read_next_atom(parser, TRUE, tag_r);
+}
+
+int imap_parser_read_command_name(struct imap_parser *parser,
+ const char **name_r)
+{
+ return imap_parser_read_next_atom(parser, FALSE, name_r);
+}
diff --git a/src/lib-imap/imap-parser.h b/src/lib-imap/imap-parser.h
index e5d01c17f2..5e09d61d2b 100644
--- a/src/lib-imap/imap-parser.h
+++ b/src/lib-imap/imap-parser.h
@@ -101,5 +101,12 @@ int imap_parser_finish_line(struct imap_parser *parser, unsigned int count,
/* Read one word - used for reading tag and command name.
Returns NULL if more data is needed. */
const char *imap_parser_read_word(struct imap_parser *parser);
+/* Read command tag. Returns 1 if tag was returned, 0 if more data is needed,
+ -1 if input isn't a valid tag. */
+int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r);
+/* Read command name. Returns 1 if command name was returned, 0 if more data is
+ needed, -1 if input isn't a valid command name string. */
+int imap_parser_read_command_name(struct imap_parser *parser,
+ const char **name_r);
#endif
diff --git a/src/lib-imap/test-imap-parser.c b/src/lib-imap/test-imap-parser.c
index 93ef8fd59b..3ca4e34858 100644
--- a/src/lib-imap/test-imap-parser.c
+++ b/src/lib-imap/test-imap-parser.c
@@ -79,10 +79,77 @@ static void test_imap_parser_partial_list(void)
test_end();
}
+static void test_imap_parser_read_tag_cmd(void)
+{
+ enum read_type {
+ BOTH,
+ TAG,
+ COMMAND
+ };
+ struct {
+ const char *input;
+ const char *tag;
+ int ret;
+ enum read_type type;
+ } tests[] = {
+ { "tag foo", "tag", 1, BOTH },
+ { "tag\r", "tag", 1, BOTH },
+ { "tag\rfoo", "tag", 1, BOTH },
+ { "tag\nfoo", "tag", 1, BOTH },
+ { "tag\r\nfoo", "tag", 1, BOTH },
+ { "\n", NULL, -1, BOTH },
+ { "tag", NULL, 0, BOTH },
+ { "tag\t", NULL, -1, BOTH },
+ { "tag\001", NULL, -1, BOTH },
+ { "tag\x80", NULL, -1, BOTH },
+ { "tag(", NULL, -1, BOTH },
+ { "tag)", NULL, -1, BOTH },
+ { "tag{", NULL, -1, BOTH },
+ { "tag/ ", "tag/", 1, BOTH },
+ { "tag%", NULL, -1, BOTH },
+ { "tag*", NULL, -1, BOTH },
+ { "tag\"", NULL, -1, BOTH },
+ { "tag\\", NULL, -1, BOTH },
+ { "tag+", NULL, -1, TAG },
+ { "tag+ ", "tag+", 1, COMMAND },
+ };
+ struct istream *input;
+ struct imap_parser *parser;
+ const char *atom;
+ int ret;
+
+ test_begin("imap_parser_read_tag and imap_parser_read_command_name");
+ for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) {
+ if (tests[i].type != COMMAND) {
+ input = test_istream_create(tests[i].input);
+ test_assert(i_stream_read(input) > 0);
+ parser = imap_parser_create(input, NULL, 1024);
+ ret = imap_parser_read_tag(parser, &atom);
+ test_assert_idx(ret == tests[i].ret, i);
+ test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
+ imap_parser_unref(&parser);
+ i_stream_destroy(&input);
+ }
+
+ if (tests[i].type != TAG) {
+ input = test_istream_create(tests[i].input);
+ test_assert(i_stream_read(input) > 0);
+ parser = imap_parser_create(input, NULL, 1024);
+ ret = imap_parser_read_command_name(parser, &atom);
+ test_assert_idx(ret == tests[i].ret, i);
+ test_assert_idx(ret <= 0 || strcmp(tests[i].tag, atom) == 0, i);
+ imap_parser_unref(&parser);
+ i_stream_destroy(&input);
+ }
+ }
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
test_imap_parser_crlf,
+ test_imap_parser_read_tag_cmd,
NULL
};
return test_run(test_functions);

View File

@ -0,0 +1,46 @@
From eea57c8683325f9767b2eb1b44a0b23352541c1e Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 17:59:19 +0300
Subject: [PATCH] imap: Split off client_command_failed_early()
---
src/imap/imap-client.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c
index 07b2a8018b..0bf03caa97 100644
--- a/src/imap/imap-client.c
+++ b/src/imap/imap-client.c
@@ -1176,6 +1176,19 @@ bool client_handle_unfinished_cmd(struct client_command_context *cmd)
return TRUE;
}
+static void
+client_command_failed_early(struct client_command_context **_cmd,
+ const char *error)
+{
+ struct client_command_context *cmd = *_cmd;
+
+ io_loop_time_refresh();
+ command_stats_start(cmd);
+ client_send_command_error(cmd, error);
+ cmd->param_error = TRUE;
+ client_command_free(_cmd);
+}
+
static bool client_command_input(struct client_command_context *cmd)
{
struct client *client = cmd->client;
@@ -1239,11 +1252,7 @@ static bool client_command_input(struct client_command_context *cmd)
if (cmd->func == NULL) {
/* unknown command */
- io_loop_time_refresh();
- command_stats_start(cmd);
- client_send_command_error(cmd, "Unknown command.");
- cmd->param_error = TRUE;
- client_command_free(&cmd);
+ client_command_failed_early(&cmd, "Unknown command.");
return TRUE;
} else {
i_assert(!client->disconnected);

View File

@ -0,0 +1,77 @@
From 0386140f61f9ba62225e90b419215f72bba6ad8b Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 18:11:36 +0300
Subject: [PATCH] imap: Use imap_parser_read_tag() and _read_command_name()
---
src/imap/imap-client.c | 33 ++++++++++++++++++++++-----------
1 file changed, 22 insertions(+), 11 deletions(-)
diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c
index 0bf03caa97..95e57dbf53 100644
--- a/src/imap/imap-client.c
+++ b/src/imap/imap-client.c
@@ -1182,6 +1182,9 @@ client_command_failed_early(struct client_command_context **_cmd,
{
struct client_command_context *cmd = *_cmd;
+ /* ignore the rest of this line */
+ cmd->client->input_skip_line = TRUE;
+
io_loop_time_refresh();
command_stats_start(cmd);
client_send_command_error(cmd, error);
@@ -1193,6 +1196,8 @@ static bool client_command_input(struct client_command_context *cmd)
{
struct client *client = cmd->client;
struct command *command;
+ const char *tag, *name;
+ int ret;
if (cmd->func != NULL) {
/* command is being executed - continue it */
@@ -1207,27 +1212,33 @@ static bool client_command_input(struct client_command_context *cmd)
}
if (cmd->tag == NULL) {
- cmd->tag = imap_parser_read_word(cmd->parser);
- if (cmd->tag == NULL)
+ ret = imap_parser_read_tag(cmd->parser, &tag);
+ if (ret == 0)
return FALSE; /* need more data */
- cmd->tag = p_strdup(cmd->pool, cmd->tag);
+ if (ret < 0) {
+ client_command_failed_early(&cmd, "Invalid tag.");
+ return TRUE;
+ }
+ cmd->tag = p_strdup(cmd->pool, tag);
}
if (cmd->name == NULL) {
- cmd->name = imap_parser_read_word(cmd->parser);
- if (cmd->name == NULL)
+ ret = imap_parser_read_command_name(cmd->parser, &name);
+ if (ret == 0)
return FALSE; /* need more data */
+ if (ret < 0) {
+ client_command_failed_early(&cmd, "Invalid command name.");
+ return TRUE;
+ }
/* UID commands are a special case. better to handle them
here. */
- if (!cmd->uid && strcasecmp(cmd->name, "UID") == 0) {
+ if (!cmd->uid && strcasecmp(name, "UID") == 0) {
cmd->uid = TRUE;
- cmd->name = imap_parser_read_word(cmd->parser);
- if (cmd->name == NULL)
- return FALSE; /* need more data */
+ return client_command_input(cmd);
}
- cmd->name = !cmd->uid ? p_strdup(cmd->pool, cmd->name) :
- p_strconcat(cmd->pool, "UID ", cmd->name, NULL);
+ cmd->name = !cmd->uid ? p_strdup(cmd->pool, name) :
+ p_strconcat(cmd->pool, "UID ", name, NULL);
client_command_init_finished(cmd);
imap_refresh_proctitle();
}

View File

@ -0,0 +1,55 @@
From 62061e8cf68f506c0ccaaba21fd4174764ca875f Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 18:15:35 +0300
Subject: [PATCH] imap-login: Split off client_invalid_command()
---
src/imap-login/imap-login-client.c | 27 +++++++++++++++++----------
1 file changed, 17 insertions(+), 10 deletions(-)
diff --git a/src/imap-login/imap-login-client.c b/src/imap-login/imap-login-client.c
index e2af176309..ce5049d567 100644
--- a/src/imap-login/imap-login-client.c
+++ b/src/imap-login/imap-login-client.c
@@ -194,6 +194,22 @@ static int client_command_execute(struct imap_client *client, const char *cmd,
return login_cmd->func(client, args);
}
+static bool client_invalid_command(struct imap_client *client)
+{
+ if (*client->cmd_tag == '\0')
+ client->cmd_tag = "*";
+ if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
+ client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
+ "Too many invalid IMAP commands.");
+ client_destroy(&client->common,
+ "Disconnected: Too many invalid commands");
+ return FALSE;
+ }
+ client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
+ "Error in IMAP command received by server.");
+ return TRUE;
+}
+
static bool imap_is_valid_tag(const char *tag)
{
for (; *tag != '\0'; tag++) {
@@ -326,17 +342,8 @@ static bool imap_client_input_next_cmd(struct client *_client)
"not the command name. Add that before the command, "
"like: a login user pass");
} else if (ret < 0) {
- if (*client->cmd_tag == '\0')
- client->cmd_tag = "*";
- if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
- client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
- "Too many invalid IMAP commands.");
- client_destroy(&client->common,
- "Disconnected: Too many invalid commands");
+ if (!client_invalid_command(client))
return FALSE;
- }
- client_send_reply(&client->common, IMAP_CMD_REPLY_BAD,
- "Error in IMAP command received by server.");
}
return ret != 0 && !client->common.destroyed;

View File

@ -0,0 +1,110 @@
From 9d3ecff3de5553159334cf644e996a616dc52670 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 18:22:42 +0300
Subject: [PATCH] imap-login: Use imap_parser_read_tag() and
_read_command_name()
---
src/imap-login/imap-login-client.c | 58 ++++++++++++------------------
1 file changed, 23 insertions(+), 35 deletions(-)
diff --git a/src/imap-login/imap-login-client.c b/src/imap-login/imap-login-client.c
index ce5049d567..b2c8af9cbf 100644
--- a/src/imap-login/imap-login-client.c
+++ b/src/imap-login/imap-login-client.c
@@ -196,7 +196,7 @@ static int client_command_execute(struct imap_client *client, const char *cmd,
static bool client_invalid_command(struct imap_client *client)
{
- if (*client->cmd_tag == '\0')
+ if (client->cmd_tag == NULL || *client->cmd_tag == '\0')
client->cmd_tag = "*";
if (++client->common.bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
client_send_reply(&client->common, IMAP_CMD_REPLY_BYE,
@@ -210,33 +210,6 @@ static bool client_invalid_command(struct imap_client *client)
return TRUE;
}
-static bool imap_is_valid_tag(const char *tag)
-{
- for (; *tag != '\0'; tag++) {
- switch (*tag) {
- case '+':
- /* atom-specials: */
- case '(':
- case ')':
- case '{':
- case '/':
- case ' ':
- /* list-wildcards: */
- case '%':
- case '*':
- /* quoted-specials: */
- case '"':
- case '\\':
- return FALSE;
- default:
- if (*tag < ' ') /* CTL */
- return FALSE;
- break;
- }
- }
- return TRUE;
-}
-
static int client_parse_command(struct imap_client *client,
const struct imap_arg **args_r)
{
@@ -261,6 +234,9 @@ static int client_parse_command(struct imap_client *client,
static bool client_handle_input(struct imap_client *client)
{
+ const char *tag, *name;
+ int ret;
+
i_assert(!client->common.authenticating);
if (client->cmd_finished) {
@@ -282,23 +258,35 @@ static bool client_handle_input(struct imap_client *client)
}
if (client->cmd_tag == NULL) {
- client->cmd_tag = imap_parser_read_word(client->parser);
- if (client->cmd_tag == NULL)
+ ret = imap_parser_read_tag(client->parser, &tag);
+ if (ret == 0)
return FALSE; /* need more data */
- if (!imap_is_valid_tag(client->cmd_tag) ||
- strlen(client->cmd_tag) > IMAP_TAG_MAX_LEN) {
+ if (ret < 0 || strlen(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
to access us using HTTP protocol. */
- client->cmd_tag = "";
+ client->skip_line = TRUE;
+ client->cmd_finished = TRUE;
+ if (!client_invalid_command(client))
+ return FALSE;
+ return client_handle_input(client);
}
+ client->cmd_tag = tag;
}
if (client->cmd_name == NULL) {
- client->cmd_name = imap_parser_read_word(client->parser);
- if (client->cmd_name == NULL)
+ ret = imap_parser_read_command_name(client->parser, &name);
+ if (ret == 0)
return FALSE; /* need more data */
+ if (ret < 0) {
+ client->skip_line = TRUE;
+ client->cmd_finished = TRUE;
+ if (!client_invalid_command(client))
+ return FALSE;
+ return client_handle_input(client);
+ }
+ client->cmd_name = name;
}
return client->common.v.input_next_cmd(&client->common);
}

View File

@ -0,0 +1,39 @@
From 7a70f01fe8084431901433a2f74cb9c70fd00568 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Mon, 17 Aug 2020 18:26:01 +0300
Subject: [PATCH] lib-imap: Add imap_parser_client_read_tag()
---
src/lib-imap/imap-parser.c | 6 ++++++
src/lib-imap/imap-parser.h | 5 +++++
2 files changed, 11 insertions(+)
diff --git a/src/lib-imap/imap-parser.c b/src/lib-imap/imap-parser.c
index 52d79282fa..cc283f5c06 100644
--- a/src/lib-imap/imap-parser.c
+++ b/src/lib-imap/imap-parser.c
@@ -1014,3 +1014,9 @@ int imap_parser_read_command_name(struct imap_parser *parser,
{
return imap_parser_read_next_atom(parser, FALSE, name_r);
}
+
+int imap_parser_client_read_tag(struct imap_parser *parser,
+ const char **tag_r)
+{
+ return imap_parser_read_next_atom(parser, FALSE, tag_r);
+}
diff --git a/src/lib-imap/imap-parser.h b/src/lib-imap/imap-parser.h
index 5e09d61d2b..cd3748c00f 100644
--- a/src/lib-imap/imap-parser.h
+++ b/src/lib-imap/imap-parser.h
@@ -108,5 +108,10 @@ int imap_parser_read_tag(struct imap_parser *parser, const char **tag_r);
needed, -1 if input isn't a valid command name string. */
int imap_parser_read_command_name(struct imap_parser *parser,
const char **name_r);
+/* For IMAP clients: Read the command tag, which could also be "+" or "*".
+ Returns 1 if tag was returned, 0 if more data is needed, -1 if input isn't
+ valid. */
+int imap_parser_client_read_tag(struct imap_parser *parser,
+ const char **tag_r);
#endif

View File

@ -0,0 +1,126 @@
From fb97a1cddbda4019e327fa736972a1c7433fedaa Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Fri, 11 Sep 2020 09:53:03 +0300
Subject: [PATCH] lib-mail: message-parser - Fix assert-crash when enforcing
MIME part limit
The limit could have been exceeded with message/rfc822 parts.
---
src/lib-mail/message-parser.c | 3 +-
src/lib-mail/test-message-parser.c | 82 ++++++++++++++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c
index 6ab4c3266f..40a504da0a 100644
--- a/src/lib-mail/message-parser.c
+++ b/src/lib-mail/message-parser.c
@@ -703,7 +703,8 @@ static int parse_next_header(struct message_parser_ctx *ctx,
ctx->multipart = FALSE;
ctx->parse_next_block = parse_next_body_to_boundary;
} else if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0 &&
- !parse_too_many_nested_mime_parts(ctx)) {
+ !parse_too_many_nested_mime_parts(ctx) &&
+ ctx->total_parts_count < ctx->max_total_mime_parts) {
ctx->parse_next_block = parse_next_body_message_rfc822_init;
} else {
part->flags &= ~MESSAGE_PART_FLAG_MESSAGE_RFC822;
diff --git a/src/lib-mail/test-message-parser.c b/src/lib-mail/test-message-parser.c
index 8c5a3404f1..c4e117afc7 100644
--- a/src/lib-mail/test-message-parser.c
+++ b/src/lib-mail/test-message-parser.c
@@ -1127,6 +1127,87 @@ static const char input_msg[] =
test_end();
}
+static void test_message_parser_mime_part_limit_rfc822(void)
+{
+static const char input_msg[] =
+"Content-Type: multipart/mixed; boundary=\"1\"\n"
+"\n"
+"--1\n"
+"Content-Type: multipart/mixed; boundary=\"2\"\n"
+"\n"
+"--2\n"
+"Content-Type: message/rfc822\n"
+"\n"
+"Content-Type: text/plain\n"
+"\n"
+"1\n"
+"--2\n"
+"Content-Type: message/rfc822\n"
+"\n"
+"Content-Type: text/plain\n"
+"\n"
+"22\n"
+"--1\n"
+"Content-Type: message/rfc822\n"
+"\n"
+"Content-Type: text/plain\n"
+"\n"
+"333\n";
+ const struct message_parser_settings parser_set = {
+ .max_total_mime_parts = 3,
+ };
+ struct message_parser_ctx *parser;
+ struct istream *input;
+ struct message_part *parts, *part;
+ struct message_block block;
+ pool_t pool;
+ int ret;
+
+ test_begin("message parser mime part limit rfc822");
+ pool = pool_alloconly_create("message parser", 10240);
+ input = test_istream_create(input_msg);
+
+ parser = message_parser_init(pool, input, &parser_set);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ test_assert(ret < 0);
+ message_parser_deinit(&parser, &parts);
+
+ part = parts;
+ test_assert(part->children_count == 2);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 21);
+ test_assert(part->body_size.physical_size == 238);
+ test_assert(part->body_size.virtual_size == 238+21);
+
+ part = parts->children;
+ test_assert(part->children_count == 1);
+ test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 45);
+ test_assert(part->header_size.virtual_size == 45+2);
+ test_assert(part->body_size.lines == 18);
+ test_assert(part->body_size.physical_size == 189);
+ test_assert(part->body_size.virtual_size == 189+18);
+
+ part = parts->children->children;
+ test_assert(part->children_count == 0);
+ test_assert(part->flags == MESSAGE_PART_FLAG_IS_MIME);
+ test_assert(part->header_size.lines == 2);
+ test_assert(part->header_size.physical_size == 30);
+ test_assert(part->header_size.virtual_size == 30+2);
+ test_assert(part->body_size.lines == 15);
+ test_assert(part->body_size.physical_size == 155);
+ test_assert(part->body_size.virtual_size == 155+15);
+
+ test_parsed_parts(input, parts);
+ i_stream_unref(&input);
+ pool_unref(&pool);
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
@@ -1301,6 +1382,7 @@ int main(void)
test_message_parser_mime_part_nested_limit,
test_message_parser_mime_part_nested_limit_rfc822,
test_message_parser_mime_part_limit,
+ test_message_parser_mime_part_limit_rfc822,
NULL
};
return test_run(test_functions);

View File

@ -0,0 +1,63 @@
From 266e54b7b8c34c9a58dd60a2e53c5ca7d1deae19 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Fri, 11 Sep 2020 10:57:51 +0300
Subject: [PATCH] lib-imap: Don't generate invalid BODYSTRUCTURE when reaching
MIME part limit
If the last MIME part was message/rfc822 and its child was truncated away,
BODYSTRUCTURE was missing the ENVELOPE and BODY[STRUCTURE] parts. Fixed by
writing empty dummy ones.
---
src/lib-imap/imap-bodystructure.c | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c
index 4e379e56a9..e3da1090b4 100644
--- a/src/lib-imap/imap-bodystructure.c
+++ b/src/lib-imap/imap-bodystructure.c
@@ -146,11 +146,25 @@ static void part_write_body(const struct message_part *part,
string_t *str, bool extended)
{
const struct message_part_data *data = part->data;
- bool text;
+ bool text, message_rfc822;
i_assert(part->data != NULL);
- if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0) {
+ if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)
+ message_rfc822 = TRUE;
+ else if (data->content_type != NULL &&
+ strcasecmp(data->content_type, "message") == 0 &&
+ strcasecmp(data->content_subtype, "rfc822") == 0) {
+ /* It's message/rfc822, but without
+ MESSAGE_PART_FLAG_MESSAGE_RFC822. That likely means maximum
+ MIME part count was reached while parsing the mail. Write
+ the missing child mail's ENVELOPE and BODY as empty dummy
+ values. */
+ message_rfc822 = TRUE;
+ } else
+ message_rfc822 = FALSE;
+
+ if (message_rfc822) {
str_append(str, "\"message\" \"rfc822\"");
text = FALSE;
} else {
@@ -200,6 +214,17 @@ static void part_write_body(const struct message_part *part,
part_write_bodystructure_siblings(part->children, str, extended);
str_printfa(str, " %u", part->body_size.lines);
+ } else if (message_rfc822) {
+ /* truncated MIME part - write out dummy values */
+ i_assert(part->children == NULL);
+
+ str_append(str, " (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) ");
+
+ if (!extended)
+ str_append(str, EMPTY_BODY);
+ else
+ str_append(str, EMPTY_BODYSTRUCTURE);
+ str_printfa(str, " %u", part->body_size.lines);
}
if (!extended)

View File

@ -0,0 +1,228 @@
From 530c1e950a1bb46ff4e4a7c8e4b7cd945ff28916 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Wed, 18 Nov 2020 18:55:34 +0200
Subject: [PATCH] lib-imap: Fix writing BODYSTRUCTURE for truncated
message/rfc822 part
If the max nesting limit is reached, write the last part out as
application/octet-stream instead of dummy message/rfc822.
Fixes error while parsing BODYSTRUCTURE:
message_part message/rfc822 flag doesn't match BODYSTRUCTURE
---
src/lib-imap/imap-bodystructure.c | 54 +++++++++----------
src/lib-imap/test-imap-bodystructure.c | 73 ++++++++++++++++++++++++--
2 files changed, 96 insertions(+), 31 deletions(-)
diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c
index e3da1090b4..ab422c00d2 100644
--- a/src/lib-imap/imap-bodystructure.c
+++ b/src/lib-imap/imap-bodystructure.c
@@ -142,31 +142,42 @@ static void part_write_body_multipart(const struct message_part *part,
part_write_bodystructure_common(data, str);
}
+static bool part_is_truncated(const struct message_part *part)
+{
+ const struct message_part_data *data = part->data;
+
+ i_assert((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0);
+
+ if (data->content_type != NULL) {
+ if (strcasecmp(data->content_type, "message") == 0 &&
+ strcasecmp(data->content_subtype, "rfc822") == 0) {
+ /* It's message/rfc822, but without
+ MESSAGE_PART_FLAG_MESSAGE_RFC822. */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
static void part_write_body(const struct message_part *part,
string_t *str, bool extended)
{
const struct message_part_data *data = part->data;
- bool text, message_rfc822;
+ bool text;
i_assert(part->data != NULL);
- if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)
- message_rfc822 = TRUE;
- else if (data->content_type != NULL &&
- strcasecmp(data->content_type, "message") == 0 &&
- strcasecmp(data->content_subtype, "rfc822") == 0) {
- /* It's message/rfc822, but without
- MESSAGE_PART_FLAG_MESSAGE_RFC822. That likely means maximum
- MIME part count was reached while parsing the mail. Write
- the missing child mail's ENVELOPE and BODY as empty dummy
- values. */
- message_rfc822 = TRUE;
- } else
- message_rfc822 = FALSE;
-
- if (message_rfc822) {
+ if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0) {
str_append(str, "\"message\" \"rfc822\"");
text = FALSE;
+ } else if (part_is_truncated(part)) {
+ /* Maximum MIME part count was reached while parsing the mail.
+ Write this part out as application/octet-stream instead.
+ We're not using text/plain, because it would require
+ message-parser to use MESSAGE_PART_FLAG_TEXT for this part
+ to avoid losing line count in message_part serialization. */
+ str_append(str, "\"application\" \"octet-stream\"");
+ text = FALSE;
} else {
/* "content type" "subtype" */
if (data->content_type == NULL) {
@@ -214,17 +225,6 @@ static void part_write_body(const struct message_part *part,
part_write_bodystructure_siblings(part->children, str, extended);
str_printfa(str, " %u", part->body_size.lines);
- } else if (message_rfc822) {
- /* truncated MIME part - write out dummy values */
- i_assert(part->children == NULL);
-
- str_append(str, " (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) ");
-
- if (!extended)
- str_append(str, EMPTY_BODY);
- else
- str_append(str, EMPTY_BODYSTRUCTURE);
- str_printfa(str, " %u", part->body_size.lines);
}
if (!extended)
diff --git a/src/lib-imap/test-imap-bodystructure.c b/src/lib-imap/test-imap-bodystructure.c
index dfc9957488..6cb699e126 100644
--- a/src/lib-imap/test-imap-bodystructure.c
+++ b/src/lib-imap/test-imap-bodystructure.c
@@ -4,6 +4,7 @@
#include "istream.h"
#include "str.h"
#include "message-part-data.h"
+#include "message-part-serialize.h"
#include "message-parser.h"
#include "imap-bodystructure.h"
#include "test-common.h"
@@ -379,12 +380,14 @@ struct normalize_test normalize_tests[] = {
static const unsigned int normalize_tests_count = N_ELEMENTS(normalize_tests);
static struct message_part *
-msg_parse(pool_t pool, const char *message, bool parse_bodystructure)
+msg_parse(pool_t pool, const char *message, unsigned int max_nested_mime_parts,
+ bool parse_bodystructure)
{
const struct message_parser_settings parser_set = {
.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ .max_nested_mime_parts = max_nested_mime_parts,
};
struct message_parser_ctx *parser;
struct istream *input;
@@ -418,7 +421,7 @@ static void test_imap_bodystructure_write(void)
pool_t pool = pool_alloconly_create("imap bodystructure write", 1024);
test_begin(t_strdup_printf("imap bodystructure write [%u]", i));
- parts = msg_parse(pool, test->message, TRUE);
+ parts = msg_parse(pool, test->message, 0, TRUE);
imap_bodystructure_write(parts, str, TRUE);
test_assert(strcmp(str_c(str), test->bodystructure) == 0);
@@ -445,7 +448,7 @@ static void test_imap_bodystructure_parse(void)
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
test_begin(t_strdup_printf("imap bodystructure parser [%u]", i));
- parts = msg_parse(pool, test->message, FALSE);
+ parts = msg_parse(pool, test->message, 0, FALSE);
test_assert(imap_body_parse_from_bodystructure(test->bodystructure,
str, &error) == 0);
@@ -512,7 +515,7 @@ static void test_imap_bodystructure_normalize(void)
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
test_begin(t_strdup_printf("imap bodystructure normalize [%u]", i));
- parts = msg_parse(pool, test->message, FALSE);
+ parts = msg_parse(pool, test->message, 0, FALSE);
ret = imap_bodystructure_parse(test->input,
pool, parts, &error);
@@ -531,6 +534,67 @@ static void test_imap_bodystructure_normalize(void)
} T_END;
}
+static const struct {
+ const char *input;
+ const char *bodystructure;
+ unsigned int max_depth;
+} truncation_tests[] = {
+ {
+ .input = "Content-Type: message/rfc822\n"
+ "\n"
+ "Content-Type: message/rfc822\n"
+ "Header2: value2\n"
+ "\n"
+ "Subject: hello world\n"
+ "Header2: value2\n"
+ "Header3: value3\n"
+ "\n"
+ "body line 1\n"
+ "body line 2\n"
+ "body line 4\n"
+ "body line 3\n",
+ .bodystructure = "\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 159 (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"application\" \"octet-stream\" NIL NIL NIL \"7bit\" 110 NIL NIL NIL NIL) 11 NIL NIL NIL NIL",
+ .max_depth = 2,
+ },
+};
+
+static void test_imap_bodystructure_truncation(void)
+{
+ struct message_part *parts;
+ const char *error;
+ string_t *str_body = t_str_new(128);
+ string_t *str_parts = t_str_new(128);
+ pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
+
+ test_begin("imap bodystructure truncation");
+
+ for (unsigned int i = 0; i < N_ELEMENTS(truncation_tests); i++) {
+ p_clear(pool);
+ str_truncate(str_body, 0);
+ str_truncate(str_parts, 0);
+
+ parts = msg_parse(pool, truncation_tests[i].input,
+ truncation_tests[i].max_depth,
+ TRUE);
+
+ /* write out BODYSTRUCTURE and serialize message_parts */
+ imap_bodystructure_write(parts, str_body, TRUE);
+ message_part_serialize(parts, str_parts);
+
+ /* now deserialize message_parts and make sure they can be used
+ to parse BODYSTRUCTURE */
+ parts = message_part_deserialize(pool, str_data(str_parts),
+ str_len(str_parts), &error);
+ test_assert(parts != NULL);
+ test_assert(imap_bodystructure_parse(str_c(str_body), pool,
+ parts, &error) == 0);
+ test_assert_strcmp(str_c(str_body),
+ truncation_tests[i].bodystructure);
+ }
+ pool_unref(&pool);
+ test_end();
+}
+
int main(void)
{
static void (*const test_functions[])(void) = {
@@ -538,6 +602,7 @@ int main(void)
test_imap_bodystructure_parse,
test_imap_bodystructure_normalize,
test_imap_bodystructure_parse_full,
+ test_imap_bodystructure_truncation,
NULL
};
return test_run(test_functions);

View File

@ -0,0 +1,64 @@
From ec2c5ffde7a1ca63219d47831725599e7de76f7f Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Wed, 18 Nov 2020 20:48:11 +0200
Subject: [PATCH] lib-imap: Fix writing BODYSTRUCTURE for truncated multipart/
part
If the max nesting limit is reached, write the last part out as
application/octet-stream. The original content-type could be confusing
IMAP clients when they don't see any child parts.
---
src/lib-imap/imap-bodystructure.c | 6 ++++++
src/lib-imap/test-imap-bodystructure.c | 15 +++++++++++++++
2 files changed, 21 insertions(+)
diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c
index ab422c00d2..bfb6e64197 100644
--- a/src/lib-imap/imap-bodystructure.c
+++ b/src/lib-imap/imap-bodystructure.c
@@ -147,6 +147,7 @@ static bool part_is_truncated(const struct message_part *part)
const struct message_part_data *data = part->data;
i_assert((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0);
+ i_assert((part->flags & MESSAGE_PART_FLAG_MULTIPART) == 0);
if (data->content_type != NULL) {
if (strcasecmp(data->content_type, "message") == 0 &&
@@ -155,6 +156,11 @@ static bool part_is_truncated(const struct message_part *part)
MESSAGE_PART_FLAG_MESSAGE_RFC822. */
return TRUE;
}
+ if (strcasecmp(data->content_type, "multipart") == 0) {
+ /* It's multipart/, but without
+ MESSAGE_PART_FLAG_MULTIPART. */
+ return TRUE;
+ }
}
return FALSE;
}
diff --git a/src/lib-imap/test-imap-bodystructure.c b/src/lib-imap/test-imap-bodystructure.c
index 6cb699e126..2118907e78 100644
--- a/src/lib-imap/test-imap-bodystructure.c
+++ b/src/lib-imap/test-imap-bodystructure.c
@@ -556,6 +556,21 @@ static const struct {
.bodystructure = "\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 159 (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"application\" \"octet-stream\" NIL NIL NIL \"7bit\" 110 NIL NIL NIL NIL) 11 NIL NIL NIL NIL",
.max_depth = 2,
},
+ {
+ .input = "Content-Type: multipart/mixed; boundary=1\n"
+ "\n"
+ "--1\n"
+ "Content-Type: multipart/mixed; boundary=2\n"
+ "\n"
+ "--2\n"
+ "Content-Type: multipart/mixed; boundary=3\n"
+ "\n"
+ "--3\n"
+ "\n"
+ "body\n",
+ .bodystructure = "(\"application\" \"octet-stream\" (\"boundary\" \"2\") NIL NIL \"7bit\" 63 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"1\") NIL NIL NIL",
+ .max_depth = 2,
+ },
};
static void test_imap_bodystructure_truncation(void)

View File

@ -0,0 +1,127 @@
From a912198bdc38421ad84044089db84fc14c69c228 Mon Sep 17 00:00:00 2001
From: Timo Sirainen <timo.sirainen@open-xchange.com>
Date: Wed, 18 Nov 2020 21:22:45 +0200
Subject: [PATCH] lib-imap: Fix writing BODYSTRUCTURE for truncated
multipart/digest part
Fixes error while parsing BODYSTRUCTURE:
message_part message/rfc822 flag doesn't match lines in BODYSTRUCTURE
---
src/lib-imap/imap-bodystructure.c | 9 +++++++++
src/lib-imap/test-imap-bodystructure.c | 28 ++++++++++++++++++++++----
2 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c
index bfb6e64197..5d2e5a3a84 100644
--- a/src/lib-imap/imap-bodystructure.c
+++ b/src/lib-imap/imap-bodystructure.c
@@ -161,6 +161,14 @@ static bool part_is_truncated(const struct message_part *part)
MESSAGE_PART_FLAG_MULTIPART. */
return TRUE;
}
+ } else {
+ /* No Content-Type */
+ if (part->parent != NULL &&
+ (part->parent->flags & MESSAGE_PART_FLAG_MULTIPART_DIGEST) != 0) {
+ /* Parent is MESSAGE_PART_FLAG_MULTIPART_DIGEST
+ (so this should have been message/rfc822). */
+ return TRUE;
+ }
}
return FALSE;
}
@@ -195,6 +203,7 @@ static void part_write_body(const struct message_part *part,
str_append_c(str, ' ');
imap_append_string(str, data->content_subtype);
}
+ i_assert(text == ((part->flags & MESSAGE_PART_FLAG_TEXT) != 0));
}
/* ("content type param key" "value" ...) */
diff --git a/src/lib-imap/test-imap-bodystructure.c b/src/lib-imap/test-imap-bodystructure.c
index 2118907e78..0f70cb0035 100644
--- a/src/lib-imap/test-imap-bodystructure.c
+++ b/src/lib-imap/test-imap-bodystructure.c
@@ -381,13 +381,14 @@ static const unsigned int normalize_tests_count = N_ELEMENTS(normalize_tests);
static struct message_part *
msg_parse(pool_t pool, const char *message, unsigned int max_nested_mime_parts,
- bool parse_bodystructure)
+ unsigned int max_total_mime_parts, bool parse_bodystructure)
{
const struct message_parser_settings parser_set = {
.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
.max_nested_mime_parts = max_nested_mime_parts,
+ .max_total_mime_parts = max_total_mime_parts,
};
struct message_parser_ctx *parser;
struct istream *input;
@@ -421,7 +422,7 @@ static void test_imap_bodystructure_write(void)
pool_t pool = pool_alloconly_create("imap bodystructure write", 1024);
test_begin(t_strdup_printf("imap bodystructure write [%u]", i));
- parts = msg_parse(pool, test->message, 0, TRUE);
+ parts = msg_parse(pool, test->message, 0, 0, TRUE);
imap_bodystructure_write(parts, str, TRUE);
test_assert(strcmp(str_c(str), test->bodystructure) == 0);
@@ -448,7 +449,7 @@ static void test_imap_bodystructure_parse(void)
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
test_begin(t_strdup_printf("imap bodystructure parser [%u]", i));
- parts = msg_parse(pool, test->message, 0, FALSE);
+ parts = msg_parse(pool, test->message, 0, 0, FALSE);
test_assert(imap_body_parse_from_bodystructure(test->bodystructure,
str, &error) == 0);
@@ -515,7 +516,7 @@ static void test_imap_bodystructure_normalize(void)
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
test_begin(t_strdup_printf("imap bodystructure normalize [%u]", i));
- parts = msg_parse(pool, test->message, 0, FALSE);
+ parts = msg_parse(pool, test->message, 0, 0, FALSE);
ret = imap_bodystructure_parse(test->input,
pool, parts, &error);
@@ -538,6 +539,7 @@ static const struct {
const char *input;
const char *bodystructure;
unsigned int max_depth;
+ unsigned int max_total;
} truncation_tests[] = {
{
.input = "Content-Type: message/rfc822\n"
@@ -571,6 +573,23 @@ static const struct {
.bodystructure = "(\"application\" \"octet-stream\" (\"boundary\" \"2\") NIL NIL \"7bit\" 63 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"1\") NIL NIL NIL",
.max_depth = 2,
},
+ {
+ .input = "Content-Type: multipart/digest; boundary=1\n"
+ "\n"
+ "--1\n"
+ "\n"
+ "Subject: hdr1\n"
+ "\n"
+ "body1\n"
+ "--1\n"
+ "\n"
+ "Subject: hdr2\n"
+ "\n"
+ "body2\n",
+ .bodystructure = "(\"application\" \"octet-stream\" NIL NIL NIL \"7bit\" 55 NIL NIL NIL NIL) \"digest\" (\"boundary\" \"1\") NIL NIL NIL",
+ .max_total = 2,
+ },
+
};
static void test_imap_bodystructure_truncation(void)
@@ -590,6 +609,7 @@ static void test_imap_bodystructure_truncation(void)
parts = msg_parse(pool, truncation_tests[i].input,
truncation_tests[i].max_depth,
+ truncation_tests[i].max_total,
TRUE);
/* write out BODYSTRUCTURE and serialize message_parts */

View File

@ -0,0 +1,73 @@
diff --git a/src/lib-mail/message-parser.c b/src/lib-mail/message-parser.c
index 011dea9050..8baf622e59 100644
--- a/src/lib-mail/message-parser.c
+++ b/src/lib-mail/message-parser.c
@@ -138,6 +138,7 @@ message_part_append(struct message_parser_ctx *ctx)
struct message_part *parent = ctx->part;
struct message_part *part;
+ i_assert(!ctx->preparsed);
i_assert(parent != NULL);
i_assert((parent->flags & (MESSAGE_PART_FLAG_MULTIPART |
MESSAGE_PART_FLAG_MESSAGE_RFC822)) != 0);
@@ -171,12 +172,14 @@ static void message_part_finish(struct message_parser_ctx *ctx)
{
struct message_part **const *parent_next_partp;
- i_assert(ctx->nested_parts_count > 0);
- ctx->nested_parts_count--;
+ if (!ctx->preparsed) {
+ i_assert(ctx->nested_parts_count > 0);
+ ctx->nested_parts_count--;
- parent_next_partp = array_back(&ctx->next_part_stack);
- array_pop_back(&ctx->next_part_stack);
- ctx->next_part = *parent_next_partp;
+ parent_next_partp = array_back(&ctx->next_part_stack);
+ array_pop_back(&ctx->next_part_stack);
+ ctx->next_part = *parent_next_partp;
+ }
message_size_add(&ctx->part->parent->body_size, &ctx->part->body_size);
message_size_add(&ctx->part->parent->body_size, &ctx->part->header_size);
diff --git a/src/lib-mail/test-message-parser.c b/src/lib-mail/test-message-parser.c
index 13984f939e..a00f0d6200 100644
--- a/src/lib-mail/test-message-parser.c
+++ b/src/lib-mail/test-message-parser.c
@@ -178,9 +178,10 @@ static void test_message_parser_small_blocks(void)
static void test_message_parser_stop_early(void)
{
struct message_parser_ctx *parser;
- struct istream *input;
+ struct istream *input, *input2;
struct message_part *parts;
struct message_block block;
+ const char *error;
unsigned int i;
pool_t pool;
int ret;
@@ -198,6 +199,24 @@ static void test_message_parser_stop_early(void)
&block)) > 0) ;
test_assert(ret == 0);
message_parser_deinit(&parser, &parts);
+
+ /* test preparsed - first re-parse everything with a stream
+ that sees EOF at this position */
+ input2 = i_stream_create_from_data(test_msg, i);
+ parser = message_parser_init(pool, input2, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser,
+ &block)) > 0) ;
+ test_assert(ret == -1);
+ message_parser_deinit(&parser, &parts);
+
+ /* now parse from the parts */
+ i_stream_seek(input2, 0);
+ parser = message_parser_init_from_parts(parts, input2, &set_empty);
+ while ((ret = message_parser_parse_next_block(parser,
+ &block)) > 0) ;
+ test_assert(ret == -1);
+ test_assert(message_parser_deinit_from_parts(&parser, &parts, &error) == 0);
+ i_stream_unref(&input2);
}
i_stream_unref(&input);

View File

@ -0,0 +1,20 @@
diff -up dovecot-2.3.8/src/lib-storage/index/index-mail-binary.c.blockcount dovecot-2.3.8/src/lib-storage/index/index-mail-binary.c
--- dovecot-2.3.8/src/lib-storage/index/index-mail-binary.c.blockcount 2020-12-02 11:34:10.229027593 +0100
+++ dovecot-2.3.8/src/lib-storage/index/index-mail-binary.c 2020-12-02 11:36:47.328933276 +0100
@@ -339,13 +339,14 @@ blocks_count_lines(struct binary_ctx *ct
i_stream_skip(full_input, skip);
cur_block_offset += skip;
- if (cur_block->input->eof) {
+ if (i_stream_read_eof(cur_block->input)) {
/* go to the next block */
- if (++block_idx == block_count) {
+ if (block_idx+1 == block_count) {
i_assert(i_stream_read_eof(full_input));
ret = -1;
break;
}
+ block_idx++;
cur_block++;
cur_block_offset = 0;
}

View File

@ -1,2 +1,2 @@
d /var/run/dovecot 0755 root dovecot -
d /run/dovecot 0755 root dovecot -

View File

@ -5,7 +5,7 @@ Name: dovecot
Epoch: 1
Version: 2.3.8
%global prever %{nil}
Release: 4%{?dist}
Release: 9%{?dist}
#dovecot itself is MIT, a few sources are PD, pigeonhole is LGPLv2
License: MIT and LGPLv2
Group: System Environment/Daemons
@ -55,6 +55,28 @@ Patch20: dovecot-2.3.8-CVE_2020_12673.patch
Patch21: dovecot-2.3.8-CVE_2020_12674prereq.patch
Patch22: dovecot-2.3.8-CVE_2020_12674.patch
# from upstream, for dovecot <= 2.3.11.3, rhbz#1894418
Patch23: dovecot-2.3.8-blockcount.patch
# from upstream, for dovecot < 2.3.11.3, rhbz#1888111
Patch24: dovecot-2.3.8-a668d767.patch
Patch25: dovecot-2.3.13-CVE_2020_25275-part1.patch
Patch26: dovecot-2.3.13-CVE_2020_25275-part2.patch
Patch27: dovecot-2.3.13-CVE_2020_25275-part3.patch
Patch28: dovecot-2.3.13-CVE_2020_25275-part4.patch
Patch29: dovecot-2.3.13-CVE_2020_25275-part5.patch
Patch30: dovecot-2.3.13-CVE_2020_25275-part6.patch
Patch31: dovecot-2.3.13-CVE_2020_25275-part7.patch
Patch32: dovecot-2.3.13-CVE_2020_25275-part8.patch
Patch33: dovecot-2.3.13-CVE_2020_25275regr-part1.patch
Patch34: dovecot-2.3.13-CVE_2020_25275regr-part2.patch
Patch35: dovecot-2.3.13-CVE_2020_25275regr-part3.patch
Patch36: dovecot-2.3.13-CVE_2020_24386-prereq1.patch
Patch37: dovecot-2.3.13-CVE_2020_24386-part1.patch
Patch38: dovecot-2.3.13-CVE_2020_24386-part2.patch
Patch39: dovecot-2.3.13-CVE_2020_24386-part3.patch
Patch40: dovecot-2.3.13-CVE_2020_24386-part4.patch
Source15: prestartscript
BuildRequires: openssl-devel, pam-devel, zlib-devel, bzip2-devel, libcap-devel
@ -69,6 +91,7 @@ BuildRequires: krb5-devel
BuildRequires: quota-devel
BuildRequires: xz-devel
BuildRequires: lz4-devel
BuildRequires: multilib-rpm-config
#BuildRequires: libsodium-devel
#BuildRequires: libexttextcat-devel
#BuildRequires: libstemmer-devel
@ -166,6 +189,24 @@ This package provides the development files for dovecot.
%patch20 -p1 -b .CVE_2020_12673
%patch21 -p1 -b .CVE_2020_12674prereq
%patch22 -p1 -b .CVE_2020_12674
%patch23 -p1 -b .blockcount
%patch24 -p1 -b .a668d767
%patch25 -p1 -b .CVE_2020_25275-part1
%patch26 -p1 -b .CVE_2020_25275-part2
%patch27 -p1 -b .CVE_2020_25275-part3
%patch28 -p1 -b .CVE_2020_25275-part4
%patch29 -p1 -b .CVE_2020_25275-part5
%patch30 -p1 -b .CVE_2020_25275-part6
%patch31 -p1 -b .CVE_2020_25275-part7
%patch32 -p1 -b .CVE_2020_25275-part8
%patch33 -p1 -b .CVE_2020_25275regr-part1
%patch34 -p1 -b .CVE_2020_25275regr-part2
%patch35 -p1 -b .CVE_2020_25275regr-part3
%patch36 -p1 -b .CVE_2020_24386-prereq1
%patch37 -p1 -b .CVE_2020_24386-part1
%patch38 -p1 -b .CVE_2020_24386-part2
%patch39 -p1 -b .CVE_2020_24386-part3
%patch40 -p1 -b .CVE_2020_24386-part4
pushd dovecot-2*3-pigeonhole-%{pigeonholever}
popd
@ -184,6 +225,7 @@ autoreconf -I . -fiv #required for aarch64 support
%endif
%configure \
INSTALL_DATA="install -c -p -m644" \
--with-rundir=%{_rundir}/%{name} \
--docdir=%{_docdir}/%{name} \
--disable-static \
--disable-rpath \
@ -230,9 +272,11 @@ rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
#move doc dir back to build dir so doc macro in files section can use it
# move doc dir back to build dir so doc macro in files section can use it
mv $RPM_BUILD_ROOT/%{_docdir}/%{name} %{_builddir}/%{name}-%{version}%{?prever}/docinstall
# fix multilib issues
%multilib_fix_c_header --file %{_includedir}/dovecot/config.h
pushd dovecot-2*3-pigeonhole-%{pigeonholever}
make install DESTDIR=$RPM_BUILD_ROOT
@ -525,6 +569,22 @@ make check
%{_libdir}/%{name}/dict/libdriver_pgsql.so
%changelog
* Wed Feb 03 2021 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-9
- fix CVE-2020-24386 IMAP hibernation function allows mail access (#1913534)
* Tue Jan 12 2021 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-8
- fix CVE-2020-25275 denial of service via mail MIME parsing (#1914019)
* Thu Jan 07 2021 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-7
- change run directory from /var/run to /run (#1805947)
* Wed Dec 02 2020 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-6
- fix mail storage block count parsing (#1894418)
- MIME parser crashed when boundaries were wrong (#1888111)
* Mon Nov 02 2020 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-5
- multilib compatibility (#1853137)
* Fri Aug 07 2020 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-4
- fix CVE-2020-12100 resource exhaustion via deeply nested MIME parts (#1866756)
- fix CVE-2020-12673 out of bound reads in dovecot NTLM implementation (#1866761)