import dovecot-2.3.8-9.el8

This commit is contained in:
CentOS Sources 2021-03-30 09:18:40 -04:00 committed by Stepan Oksanichenko
parent 21c7747e42
commit 56c8e8bb5b
27 changed files with 4941 additions and 16 deletions

View File

@ -1,11 +0,0 @@
diff -up dovecot-2.3.0.1/src/auth/mycrypt.c.libxcrypt dovecot-2.3.0.1/src/auth/mycrypt.c
--- dovecot-2.3.0.1/src/auth/mycrypt.c.libxcrypt 2018-02-28 15:28:58.000000000 +0100
+++ dovecot-2.3.0.1/src/auth/mycrypt.c 2018-03-27 10:57:38.447769201 +0200
@@ -14,6 +14,7 @@
# define _XPG6 /* Some Solaris versions require this, some break with this */
#endif
#include <unistd.h>
+#include <crypt.h>
#include "mycrypt.h"

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 */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
diff -up dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/plugins/notify/ext-notify-common.c.CVE_2020_12100ph dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/plugins/notify/ext-notify-common.c
--- dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/plugins/notify/ext-notify-common.c.CVE_2020_12100ph 2019-10-08 10:48:14.000000000 +0200
+++ dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/plugins/notify/ext-notify-common.c 2020-08-07 16:42:56.515389867 +0200
@@ -148,6 +148,7 @@ static int cmd_notify_extract_body_text
const char **body_text_r, size_t *body_size_r)
{
const struct sieve_extension *this_ext = renv->oprtn->ext;
+ const struct message_parser_settings parser_set = { .flags = 0 };
struct ext_notify_message_context *mctx;
struct mail *mail = renv->msgdata->mail;
struct message_parser_ctx *parser;
@@ -181,7 +182,7 @@ static int cmd_notify_extract_body_text
/* Initialize body decoder */
decoder = message_decoder_init(NULL, 0);
- parser = message_parser_init(mctx->pool, input, 0, 0);
+ parser = message_parser_init(mctx->pool, input, &parser_set);
is_text = TRUE;
save_body = FALSE;
while ( (ret=message_parser_parse_next_block(parser, &block)) > 0 ) {
diff -up dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/sieve-message.c.CVE_2020_12100ph dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/sieve-message.c
--- dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/sieve-message.c.CVE_2020_12100ph 2019-10-08 10:48:14.000000000 +0200
+++ dovecot-2.3.8/dovecot-2.3-pigeonhole-0.5.8/src/lib-sieve/sieve-message.c 2020-08-07 16:42:56.516389854 +0200
@@ -1077,10 +1077,10 @@ static int sieve_message_parts_add_missi
struct sieve_message_context *msgctx = renv->msgctx;
pool_t pool = msgctx->context_pool;
struct mail *mail = sieve_message_get_mail(renv->msgctx);
- enum message_parser_flags mparser_flags =
- MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS;
- enum message_header_parser_flags hparser_flags =
- MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP;
+ struct message_parser_settings parser_set = {
+ .hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP,
+ .flags = MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS,
+ };
ARRAY(struct sieve_message_header) headers;
struct sieve_message_part *body_part, *header_part, *last_part;
struct message_parser_ctx *parser;
@@ -1117,7 +1117,7 @@ static int sieve_message_parts_add_missi
if (iter_all) {
t_array_init(&headers, 64);
hdr_content = t_str_new(512);
- hparser_flags |= MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
+ parser_set.hdr_flags |= MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
} else {
i_zero(&headers);
}
@@ -1129,7 +1129,7 @@ static int sieve_message_parts_add_missi
//parser = message_parser_init_from_parts(parts, input,
// hparser_flags, mparser_flags);
parser = message_parser_init(pool_datastack_create(),
- input, hparser_flags, mparser_flags);
+ input, &parser_set);
while ( (ret=message_parser_parse_next_block
(parser, &block)) > 0 ) {
struct sieve_message_part **body_part_idx;

View File

@ -0,0 +1,224 @@
diff -up dovecot-2.3.8/src/lib-mail/message-decoder.c.CVE_2020_12100prereq dovecot-2.3.8/src/lib-mail/message-decoder.c
--- dovecot-2.3.8/src/lib-mail/message-decoder.c.CVE_2020_12100prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/message-decoder.c 2020-08-07 17:48:58.320126698 +0200
@@ -13,9 +13,6 @@
#include "message-header-decode.h"
#include "message-decoder.h"
-/* base64 takes max 4 bytes per character, q-p takes max 3. */
-#define MAX_ENCODING_BUF_SIZE 3
-
struct message_decoder_context {
enum message_decoder_flags flags;
normalizer_func_t *normalizer;
@@ -30,7 +27,7 @@ struct message_decoder_context {
size_t translation_size;
struct qp_decoder *qp;
- buffer_t *encoding_buf;
+ struct base64_decoder base64_decoder;
char *content_type, *content_charset;
enum message_cte message_cte;
@@ -53,7 +50,7 @@ message_decoder_init(normalizer_func_t *
ctx->normalizer = normalizer;
ctx->buf = buffer_create_dynamic(default_pool, 8192);
ctx->buf2 = buffer_create_dynamic(default_pool, 8192);
- ctx->encoding_buf = buffer_create_dynamic(default_pool, 128);
+ base64_decode_init(&ctx->base64_decoder, &base64_scheme, 0);
return ctx;
}
@@ -68,7 +65,6 @@ void message_decoder_deinit(struct messa
if (ctx->qp != NULL)
qp_decoder_deinit(&ctx->qp);
- buffer_free(&ctx->encoding_buf);
buffer_free(&ctx->buf);
buffer_free(&ctx->buf2);
i_free(ctx->charset_trans_charset);
@@ -273,14 +269,9 @@ static bool message_decode_body(struct m
struct message_block *input,
struct message_block *output)
{
- struct base64_decoder b64dec;
const unsigned char *data = NULL;
- size_t pos = 0, size = 0;
+ size_t pos, size = 0;
const char *error;
- int ret;
-
- if (ctx->encoding_buf->used != 0)
- buffer_append(ctx->encoding_buf, input->data, input->size);
switch (ctx->message_cte) {
case MESSAGE_CTE_UNKNOWN:
@@ -289,12 +280,10 @@ static bool message_decode_body(struct m
case MESSAGE_CTE_78BIT:
case MESSAGE_CTE_BINARY:
- i_assert(ctx->encoding_buf->used == 0);
data = input->data;
- size = pos = input->size;
+ size = input->size;
break;
case MESSAGE_CTE_QP: {
- i_assert(ctx->encoding_buf->used == 0);
buffer_set_used_size(ctx->buf, 0);
if (ctx->qp == NULL)
ctx->qp = qp_decoder_init(ctx->buf);
@@ -302,45 +291,24 @@ static bool message_decode_body(struct m
&pos, &error);
data = ctx->buf->data;
size = ctx->buf->used;
- /* eat away all input. qp-decoder buffers it internally. */
- pos = input->size;
break;
}
case MESSAGE_CTE_BASE64:
buffer_set_used_size(ctx->buf, 0);
- base64_decode_init(&b64dec, &base64_scheme, 0);
- if (ctx->encoding_buf->used != 0) {
- ret = base64_decode_more(&b64dec,
- ctx->encoding_buf->data,
- ctx->encoding_buf->used,
- &pos, ctx->buf);
- } else {
- ret = base64_decode_more(&b64dec,
- input->data, input->size,
- &pos, ctx->buf);
- }
- if (ret < 0 || base64_decode_finish(&b64dec) < 0) {
- /* corrupted base64 data, don't bother with
- the rest of it */
- return FALSE;
- }
- if (ret == 0) {
- /* end of base64 input */
- pos = input->size;
- buffer_set_used_size(ctx->encoding_buf, 0);
+ if (!base64_decode_is_finished(&ctx->base64_decoder)) {
+ if (base64_decode_more(&ctx->base64_decoder,
+ input->data, input->size,
+ &pos, ctx->buf) <= 0) {
+ /* ignore the rest of the input in this
+ MIME part */
+ (void)base64_decode_finish(&ctx->base64_decoder);
+ }
}
data = ctx->buf->data;
size = ctx->buf->used;
break;
}
- if (ctx->encoding_buf->used != 0)
- buffer_delete(ctx->encoding_buf, 0, pos);
- else if (pos != input->size) {
- buffer_append(ctx->encoding_buf,
- input->data + pos, input->size - pos);
- }
-
if (ctx->binary_input) {
output->data = data;
output->size = size;
@@ -402,10 +370,11 @@ void message_decoder_decode_reset(struct
{
const char *error;
+ base64_decode_reset(&ctx->base64_decoder);
+
if (ctx->qp != NULL)
(void)qp_decoder_finish(ctx->qp, &error);
i_free_and_null(ctx->content_type);
i_free_and_null(ctx->content_charset);
ctx->message_cte = MESSAGE_CTE_78BIT;
- buffer_set_used_size(ctx->encoding_buf, 0);
}
diff -up dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100prereq dovecot-2.3.8/src/lib-mail/test-message-decoder.c
--- dovecot-2.3.8/src/lib-mail/test-message-decoder.c.CVE_2020_12100prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/lib-mail/test-message-decoder.c 2020-08-07 17:50:04.612248484 +0200
@@ -1,7 +1,8 @@
/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
-#include "buffer.h"
+#include "str.h"
+#include "istream.h"
#include "charset-utf8.h"
#include "message-parser.h"
#include "message-header-decode.h"
@@ -82,6 +83,66 @@ static void test_message_decoder(void)
test_end();
}
+static void test_message_decoder_multipart(void)
+{
+ static const char test_message_input[] =
+ "Content-Type: multipart/mixed; boundary=foo\n"
+ "\n"
+ "--foo\n"
+ "Content-Transfer-Encoding: quoted-printable\n"
+ "Content-Type: text/plain; charset=utf-8\n"
+ "\n"
+ "p=C3=A4iv=C3=A4=C3=A4\n"
+ "\n"
+ "--foo\n"
+ "Content-Transfer-Encoding: base64\n"
+ "Content-Type: text/plain; charset=utf-8\n"
+ "\n"
+ "ecO2dMOkIHZhYW4uCg== ignored\n"
+ "--foo\n"
+ "Content-Transfer-Encoding: base64\n"
+ "Content-Type: text/plain; charset=utf-8\n"
+ "\n"
+ "?garbage\n"
+ "--foo--\n";
+ struct message_parser_ctx *parser;
+ struct message_decoder_context *decoder;
+ struct message_part *parts;
+ struct message_block input, output;
+ struct istream *istream;
+ string_t *str_out = t_str_new(20);
+ int ret;
+
+ test_begin("message decoder multipart");
+
+ istream = test_istream_create(test_message_input);
+ parser = message_parser_init(pool_datastack_create(), istream, 0, 0);
+ decoder = message_decoder_init(NULL, 0);
+
+ test_istream_set_allow_eof(istream, FALSE);
+ for (size_t i = 0; i < sizeof(test_message_input); i++) {
+ if (i == sizeof(test_message_input)-1)
+ test_istream_set_allow_eof(istream, TRUE);
+ test_istream_set_size(istream, i);
+ while ((ret = message_parser_parse_next_block(parser, &input)) > 0) {
+ if (message_decoder_decode_next_block(decoder, &input, &output) &&
+ output.hdr == NULL && output.size > 0)
+ str_append_data(str_out, output.data, output.size);
+ }
+ if (i == sizeof(test_message_input)-1)
+ test_assert(ret == -1);
+ else
+ test_assert(ret == 0);
+ }
+ /* NOTE: qp-decoder decoder changes \n into \r\n */
+ test_assert_strcmp(str_c(str_out), "p\xC3\xA4iv\xC3\xA4\xC3\xA4\r\ny\xC3\xB6t\xC3\xA4 vaan.\n");
+
+ message_decoder_deinit(&decoder);
+ message_parser_deinit(&parser, &parts);
+ i_stream_unref(&istream);
+ test_end();
+}
+
static void test_message_decoder_current_content_type(void)
{
struct message_decoder_context *ctx;
@@ -149,6 +210,7 @@ int main(void)
{
static void (*const test_functions[])(void) = {
test_message_decoder,
+ test_message_decoder_multipart,
test_message_decoder_current_content_type,
NULL
};

View File

@ -0,0 +1,34 @@
From 1c6405d3026e5ceae3d214d63945bba85251af4c Mon Sep 17 00:00:00 2001
From: Aki Tuomi <aki.tuomi@open-xchange.com>
Date: Mon, 18 May 2020 12:33:39 +0300
Subject: [PATCH 2/3] lib-ntlm: Check buffer length on responses
Add missing check for buffer length.
If this is not checked, it is possible to send message which
causes read past buffer bug.
Broken in c7480644202e5451fbed448508ea29a25cffc99c
---
src/lib-ntlm/ntlm-message.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/lib-ntlm/ntlm-message.c b/src/lib-ntlm/ntlm-message.c
index 160b9f918c..a29413b47e 100644
--- a/src/lib-ntlm/ntlm-message.c
+++ b/src/lib-ntlm/ntlm-message.c
@@ -184,6 +184,11 @@ static bool ntlmssp_check_buffer(const struct ntlmssp_buffer *buffer,
if (length == 0 && space == 0)
return TRUE;
+ if (length > data_size) {
+ *error = "buffer length out of bounds";
+ return FALSE;
+ }
+
if (offset >= data_size) {
*error = "buffer offset out of bounds";
return FALSE;
--
2.11.0

View File

@ -0,0 +1,234 @@
From bd9d2fe7da833f0e4705a8280efc56930371806b Mon Sep 17 00:00:00 2001
From: Aki Tuomi <aki.tuomi@open-xchange.com>
Date: Wed, 6 May 2020 13:40:36 +0300
Subject: [PATCH 1/3] auth: mech-rpa - Fail on zero len buffer
---
src/auth/mech-rpa.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/auth/mech-rpa.c b/src/auth/mech-rpa.c
index 08298ebdd6..2de8705b4f 100644
--- a/src/auth/mech-rpa.c
+++ b/src/auth/mech-rpa.c
@@ -224,7 +224,7 @@ rpa_read_buffer(pool_t pool, const unsigned char **data,
return 0;
len = *p++;
- if (p + len > end)
+ if (p + len > end || len == 0)
return 0;
*buffer = p_malloc(pool, len);
--
2.11.0
From 98c39fd633adf9b1d11a7bad58ef0784a25042e6 Mon Sep 17 00:00:00 2001
From: Aki Tuomi <aki.tuomi@open-xchange.com>
Date: Mon, 18 May 2020 13:08:45 +0300
Subject: [PATCH 3/3] auth: test-mech - Add tests for RPA and NTLM bug
---
src/auth/test-mech.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+)
diff -up dovecot-2.3.8/src/auth/test-mech.c.CVE_2020_12674prereq dovecot-2.3.8/src/auth/test-mech.c
--- dovecot-2.3.8/src/auth/test-mech.c.CVE_2020_12674prereq 2020-08-07 20:46:56.095295825 +0200
+++ dovecot-2.3.8/src/auth/test-mech.c 2020-08-07 20:47:08.742124304 +0200
@@ -0,0 +1,196 @@
+/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "auth.h"
+#include "str.h"
+#include "auth-common.h"
+#include "auth-request.h"
+#include "auth-request-handler-private.h"
+#include "auth-settings.h"
+#include "otp.h"
+#include "mech-otp-skey-common.h"
+#include "settings-parser.h"
+#include "password-scheme.h"
+#include "test-common.h"
+#include "test-auth.h"
+#include "auth-token.h"
+
+#include <unistd.h>
+#include <time.h>
+
+#define UCHAR_LEN(str) (const unsigned char *)(str), sizeof(str)-1
+
+extern const struct mech_module mech_oauthbearer;
+extern const struct mech_module mech_otp;
+extern const struct mech_module mech_ntlm;
+extern const struct mech_module mech_rpa;
+
+static struct auth_settings set;
+static struct mechanisms_register *mech_reg;
+
+struct test_case {
+ const struct mech_module *mech;
+ const unsigned char *in;
+ size_t len;
+ const char *username;
+ const char *expect_error;
+ bool success;
+ bool set_username_before_test;
+ bool set_cert_username;
+};
+
+static void
+verify_plain_continue_mock_callback(struct auth_request *request,
+ verify_plain_callback_t *callback)
+{
+ request->passdb_success = TRUE;
+ callback(PASSDB_RESULT_OK, request);
+}
+
+static void
+request_handler_reply_mock_callback(struct auth_request *request,
+ enum auth_client_result result,
+ const void *auth_reply ATTR_UNUSED,
+ size_t reply_size ATTR_UNUSED)
+{
+ request->failed = result != AUTH_CLIENT_RESULT_SUCCESS;
+
+ if (request->passdb_result == PASSDB_RESULT_OK)
+ request->failed = FALSE;
+ else if (request->mech == &mech_otp) {
+ if (null_strcmp(request->user, "otp_phase_2") == 0)
+ request->failed = FALSE;
+ } else if (request->mech == &mech_oauthbearer) {
+ }
+};
+
+static void
+request_handler_reply_continue_mock_callback(struct auth_request *request,
+ const void *reply,
+ size_t reply_size)
+{
+ request->context = p_strndup(request->pool, reply, reply_size);
+}
+
+static void
+auth_client_request_mock_callback(const char *reply ATTR_UNUSED,
+ struct auth_client_connection *conn ATTR_UNUSED)
+{
+}
+
+static void test_mechs_init(void)
+{
+ const char *const services[] = {NULL};
+ process_start_time = time(NULL);
+
+ /* Copy default settings */
+ set = *(struct auth_settings *) auth_setting_parser_info.defaults;
+ global_auth_settings = &set;
+ global_auth_settings->base_dir = ".";
+ memset((&set)->username_chars_map, 1, sizeof((&set)->username_chars_map));
+ set.username_format = "";
+
+ t_array_init(&set.passdbs, 2);
+ struct auth_passdb_settings *mock_set = t_new(struct auth_passdb_settings, 1);
+ *mock_set = mock_passdb_set;
+ array_push_back(&set.passdbs, &mock_set);
+ mock_set = t_new(struct auth_passdb_settings, 1);
+ *mock_set = mock_passdb_set;
+ mock_set->master = TRUE;
+ array_push_back(&set.passdbs, &mock_set);
+ t_array_init(&set.userdbs, 1);
+
+ /* Disable stats */
+ set.stats = FALSE;
+
+ /* For tests of digest-md5. */
+ set.realms_arr = t_strsplit_spaces("example.com ", " ");
+ /* For tests of mech-anonymous. */
+ set.anonymous_username = "anonuser";
+
+ mech_init(global_auth_settings);
+ mech_reg = mech_register_init(global_auth_settings);
+ passdbs_init();
+ userdbs_init();
+ passdb_mock_mod_init();
+ password_schemes_init();
+
+ auths_preinit(&set, pool_datastack_create(), mech_reg, services);
+ auths_init();
+ auth_token_init();
+}
+
+
+static void test_rpa(void)
+{
+ test_mechs_init();
+ static struct auth_request_handler handler = {
+ .callback = auth_client_request_mock_callback,
+ .reply_callback = request_handler_reply_mock_callback,
+ .reply_continue_callback = request_handler_reply_continue_mock_callback,
+ .verify_plain_continue_callback = verify_plain_continue_mock_callback,
+ };
+
+ const struct mech_module *mech = &mech_rpa;
+ test_begin("test rpa");
+ struct auth_request *req = mech->auth_new();
+ global_auth_settings->realms_arr = t_strsplit("example.com", " ");
+ req->set = global_auth_settings;
+ req->service = "login";
+ req->handler = &handler;
+ req->mech_event = event_create(NULL);
+ req->event = event_create(NULL);
+ req->mech = mech;
+ req->state = AUTH_REQUEST_STATE_MECH_CONTINUE;
+ auth_request_state_count[AUTH_REQUEST_STATE_MECH_CONTINUE] = 1;
+ mech->auth_initial(req, UCHAR_LEN("\x60\x11\x06\x09\x60\x86\x48\x01\x86\xf8\x73\x01\x01\x01\x00\x04\x00\x00\x01"));
+ mech->auth_continue(req, UCHAR_LEN("\x60\x11\x06\x09\x60\x86\x48\x01\x86\xf8\x73\x01\x01\x00\x03A@A\x00"));
+ test_assert(req->failed == TRUE);
+ test_assert(req->passdb_success == FALSE);
+ event_unref(&req->mech_event);
+ event_unref(&req->event);
+ mech->auth_free(req);
+ test_end();
+}
+
+static void test_ntlm(void)
+{
+ static struct auth_request_handler handler = {
+ .callback = auth_client_request_mock_callback,
+ .reply_callback = request_handler_reply_mock_callback,
+ .reply_continue_callback = request_handler_reply_continue_mock_callback,
+ .verify_plain_continue_callback = verify_plain_continue_mock_callback,
+ };
+
+ const struct mech_module *mech = &mech_ntlm;
+ test_begin("test ntlm");
+ struct auth_request *req = mech->auth_new();
+ global_auth_settings->realms_arr = t_strsplit("example.com", " ");
+ req->set = global_auth_settings;
+ req->service = "login";
+ req->handler = &handler;
+ req->mech_event = event_create(NULL);
+ req->event = event_create(NULL);
+ req->mech = mech;
+ req->state = AUTH_REQUEST_STATE_MECH_CONTINUE;
+ auth_request_state_count[AUTH_REQUEST_STATE_MECH_CONTINUE] = 1;
+ mech->auth_initial(req, UCHAR_LEN("NTLMSSP\x00\x01\x00\x00\x00\x00\x02\x00\x00""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
+ mech->auth_continue(req, UCHAR_LEN("NTLMSSP\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""AA\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00""orange""\x00"));
+ test_assert(req->failed == TRUE);
+ test_assert(req->passdb_success == FALSE);
+ event_unref(&req->mech_event);
+ event_unref(&req->event);
+ mech->auth_free(req);
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_rpa,
+ test_ntlm,
+ NULL
+ };
+
+ return test_run(test_functions);
+}

View File

@ -0,0 +1,345 @@
diff -up dovecot-2.3.8/src/auth/Makefile.am.CVE_2020_12674prereq dovecot-2.3.8/src/auth/Makefile.am
--- dovecot-2.3.8/src/auth/Makefile.am.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/Makefile.am 2020-08-07 20:46:56.095295825 +0200
@@ -38,6 +38,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/src/lib-oauth2 \
-I$(top_srcdir)/src/lib-ssl-iostream \
-I$(top_srcdir)/src/lib-lua \
+ -I$(top_srcdir)/src/lib-dcrypt \
-DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \
-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
-DPKG_RUNDIR=\""$(rundir)"\" \
@@ -248,7 +249,8 @@ libstats_auth_la_SOURCES = auth-stats.c
test_programs = \
test-libpassword \
test-auth-cache \
- test-auth
+ test-auth \
+ test-mech
noinst_PROGRAMS = $(test_programs)
@@ -288,6 +290,13 @@ test_auth_SOURCES = \
test_auth_LDADD = $(test_libs) $(auth_libs) $(AUTH_LIBS)
test_auth_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs)
+test_mech_SOURCES = \
+ test-mock.c \
+ test-mech.c
+
+test_mech_LDADD = $(test_libs) $(auth_libs) $(AUTH_LIBS)
+test_mech_DEPENDENCIES = $(pkglibexec_PROGRAMS) $(test_libs)
+
check-local:
for bin in $(test_programs); do \
if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \
diff -up dovecot-2.3.8/src/auth/passdb.h.CVE_2020_12674prereq dovecot-2.3.8/src/auth/passdb.h
--- dovecot-2.3.8/src/auth/passdb.h.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/passdb.h 2020-08-07 20:35:16.295684287 +0200
@@ -24,6 +24,8 @@ enum passdb_result {
typedef void verify_plain_callback_t(enum passdb_result result,
struct auth_request *request);
+typedef void verify_plain_continue_callback_t(struct auth_request *request,
+ verify_plain_callback_t *callback);
typedef void lookup_credentials_callback_t(enum passdb_result result,
const unsigned char *credentials,
size_t size,
diff -up dovecot-2.3.8/src/auth/auth-request-handler-private.h.CVE_2020_12674prereq dovecot-2.3.8/src/auth/auth-request-handler-private.h
--- dovecot-2.3.8/src/auth/auth-request-handler-private.h.CVE_2020_12674prereq 2020-08-07 20:35:16.295684287 +0200
+++ dovecot-2.3.8/src/auth/auth-request-handler-private.h 2020-08-07 20:35:16.295684287 +0200
@@ -0,0 +1,27 @@
+#ifndef AUTH_REQUEST_HANDLER_PRIVATE_H
+#define AUTH_REQUEST_HANDLER_PRIVATE_H
+
+struct auth_request;
+struct auth_client_connection;
+
+struct auth_request_handler {
+ int refcount;
+ pool_t pool;
+ HASH_TABLE(void *, struct auth_request *) requests;
+
+ unsigned int connect_uid, client_pid;
+
+ auth_client_request_callback_t *callback;
+ struct auth_client_connection *conn;
+
+ auth_master_request_callback_t *master_callback;
+ auth_request_handler_reply_callback_t *reply_callback;
+ auth_request_handler_reply_continue_callback_t *reply_continue_callback;
+ verify_plain_continue_callback_t *verify_plain_continue_callback;
+
+ bool destroyed:1;
+ bool token_auth:1;
+};
+
+
+#endif
diff -up dovecot-2.3.8/src/auth/auth-request-handler.h.CVE_2020_12674prereq dovecot-2.3.8/src/auth/auth-request-handler.h
--- dovecot-2.3.8/src/auth/auth-request-handler.h.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/auth-request-handler.h 2020-08-07 20:35:16.295684287 +0200
@@ -17,6 +17,17 @@ auth_client_request_callback_t(const cha
typedef void
auth_master_request_callback_t(const char *reply, struct auth_master_connection *conn);
+typedef void
+auth_request_handler_reply_callback_t(struct auth_request *request,
+ enum auth_client_result result,
+ const void *auth_reply,
+ size_t reply_size);
+typedef void
+auth_request_handler_reply_continue_callback_t(struct auth_request *request,
+ const void *reply,
+ size_t reply_size);
+
+
struct auth_request_handler *
auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback,
struct auth_client_connection *conn,
diff -up dovecot-2.3.8/src/auth/test-mock.c.CVE_2020_12674prereq dovecot-2.3.8/src/auth/test-mock.c
--- dovecot-2.3.8/src/auth/test-mock.c.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/test-mock.c 2020-08-07 20:35:16.296684273 +0200
@@ -28,14 +28,22 @@ static void passdb_mock_verify_plain(str
callback(PASSDB_RESULT_OK, request);
}
+static void passdb_mock_lookup_credentials(struct auth_request *request,
+ lookup_credentials_callback_t *callback)
+{
+ passdb_handle_credentials(PASSDB_RESULT_OK, "password", "PLAIN",
+ callback, request);
+}
+
static struct passdb_module_interface mock_interface = {
.name = "mock",
.init = passdb_mock_init,
.deinit = passdb_mock_deinit,
.verify_plain = passdb_mock_verify_plain,
+ .lookup_credentials = passdb_mock_lookup_credentials,
};
-static struct auth_passdb_settings set = {
+struct auth_passdb_settings mock_passdb_set = {
.name = "mock",
.driver = "mock",
.args = "",
@@ -95,7 +103,7 @@ void passdb_mock_mod_deinit(void)
struct auth_passdb *passdb_mock(void)
{
struct auth_passdb *ret = i_new(struct auth_passdb, 1);
- ret->set = &set;
+ ret->set = &mock_passdb_set;
ret->passdb = mock_passdb_mod;
return ret;
}
diff -up dovecot-2.3.8/src/auth/test-auth.h.CVE_2020_12674prereq dovecot-2.3.8/src/auth/test-auth.h
--- dovecot-2.3.8/src/auth/test-auth.h.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/test-auth.h 2020-08-07 20:35:16.296684273 +0200
@@ -8,6 +8,8 @@
struct auth_passdb;
+extern struct auth_passdb_settings mock_passdb_set;
+
void test_auth_request_var_expand(void);
void test_db_dict_parse_cache_key(void);
void test_username_filter(void);
diff -up dovecot-2.3.8/src/auth/auth-request.c.CVE_2020_12674prereq dovecot-2.3.8/src/auth/auth-request.c
--- dovecot-2.3.8/src/auth/auth-request.c.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/auth-request.c 2020-08-07 20:35:16.295684287 +0200
@@ -16,6 +16,7 @@
#include "auth-cache.h"
#include "auth-request.h"
#include "auth-request-handler.h"
+#include "auth-request-handler-private.h"
#include "auth-request-stats.h"
#include "auth-client-connection.h"
#include "auth-master-connection.h"
@@ -67,9 +68,6 @@ static void
auth_request_userdb_import(struct auth_request *request, const char *args);
static
-void auth_request_verify_plain_continue(struct auth_request *request,
- verify_plain_callback_t *callback);
-static
void auth_request_lookup_credentials_policy_continue(struct auth_request *request,
lookup_credentials_callback_t *callback);
static
@@ -307,10 +307,12 @@ void auth_request_success_continue(struct auth_policy_check_ctx *ctx)
return;
}
- stats = auth_request_stats_get(request);
- stats->auth_success_count++;
- if (request->master_user != NULL)
- stats->auth_master_success_count++;
+ if (request->set->stats) {
+ stats = auth_request_stats_get(request);
+ stats->auth_success_count++;
+ if (request->master_user != NULL)
+ stats->auth_master_success_count++;
+ }
auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
auth_request_refresh_last_access(request);
@@ -324,8 +326,10 @@ void auth_request_fail(struct auth_request *request)
i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
- stats = auth_request_stats_get(request);
- stats->auth_failure_count++;
+ if (request->set->stats) {
+ stats = auth_request_stats_get(request);
+ stats->auth_failure_count++;
+ }
auth_request_set_state(request, AUTH_REQUEST_STATE_FINISHED);
auth_request_refresh_last_access(request);
@@ -1233,7 +1231,7 @@ void auth_request_policy_penalty_finish(
switch(ctx->type) {
case AUTH_POLICY_CHECK_TYPE_PLAIN:
- auth_request_verify_plain_continue(ctx->request, ctx->callback_plain);
+ ctx->request->handler->verify_plain_continue_callback(ctx->request, ctx->callback_plain);
return;
case AUTH_POLICY_CHECK_TYPE_LOOKUP:
auth_request_lookup_credentials_policy_continue(ctx->request, ctx->callback_lookup);
@@ -1284,7 +1282,8 @@ void auth_request_verify_plain(struct au
request->user_changed_by_lookup = FALSE;
if (request->policy_processed || !request->set->policy_check_before_auth) {
- auth_request_verify_plain_continue(request, callback);
+ request->handler->verify_plain_continue_callback(request,
+ callback);
} else {
ctx = p_new(request->pool, struct auth_policy_check_ctx, 1);
ctx->request = request;
@@ -1294,10 +1293,9 @@ void auth_request_verify_plain(struct au
}
}
-static
-void auth_request_verify_plain_continue(struct auth_request *request,
- verify_plain_callback_t *callback) {
-
+void auth_request_default_verify_plain_continue(struct auth_request *request,
+ verify_plain_callback_t *callback)
+{
struct auth_passdb *passdb;
enum passdb_result result;
const char *cache_key, *error;
diff -up dovecot-2.3.8/src/auth/auth-request-handler.c.CVE_2020_12674prereq dovecot-2.3.8/src/auth/auth-request-handler.c
--- dovecot-2.3.8/src/auth/auth-request-handler.c.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/auth-request-handler.c 2020-08-07 20:35:16.295684287 +0200
@@ -17,32 +17,28 @@
#include "auth-client-connection.h"
#include "auth-master-connection.h"
#include "auth-request-handler.h"
+#include "auth-request-handler-private.h"
#include "auth-policy.h"
#define AUTH_FAILURE_DELAY_CHECK_MSECS 500
-
-struct auth_request_handler {
- int refcount;
- pool_t pool;
- HASH_TABLE(void *, struct auth_request *) requests;
-
- unsigned int connect_uid, client_pid;
-
- auth_client_request_callback_t *callback;
- struct auth_client_connection *conn;
-
- auth_master_request_callback_t *master_callback;
-
- bool destroyed:1;
- bool token_auth:1;
-};
-
static ARRAY(struct auth_request *) auth_failures_arr;
static struct aqueue *auth_failures;
static struct timeout *to_auth_failures;
static void auth_failure_timeout(void *context) ATTR_NULL(1);
+
+static void
+auth_request_handler_default_reply_callback(struct auth_request *request,
+ enum auth_client_result result,
+ const void *auth_reply,
+ size_t reply_size);
+
+static void
+auth_request_handler_default_reply_continue(struct auth_request *request,
+ const void *reply,
+ size_t reply_size);
+
struct auth_request_handler *
auth_request_handler_create(bool token_auth, auth_client_request_callback_t *callback,
struct auth_client_connection *conn,
@@ -61,6 +57,12 @@ auth_request_handler_create(bool token_a
handler->conn = conn;
handler->master_callback = master_callback;
handler->token_auth = token_auth;
+ handler->reply_callback =
+ auth_request_handler_default_reply_callback;
+ handler->reply_continue_callback =
+ auth_request_handler_default_reply_continue;
+ handler->verify_plain_continue_callback =
+ auth_request_default_verify_plain_continue;
return handler;
}
@@ -355,6 +363,16 @@ void auth_request_handler_reply(struct a
enum auth_client_result result,
const void *auth_reply, size_t reply_size)
{
+ struct auth_request_handler *handler = request->handler;
+ handler->reply_callback(request, result, auth_reply, reply_size);
+}
+
+static void
+auth_request_handler_default_reply_callback(struct auth_request *request,
+ enum auth_client_result result,
+ const void *auth_reply,
+ size_t reply_size)
+{
struct auth_request_handler *handler = request->handler;
string_t *str;
int ret;
@@ -407,6 +425,14 @@ void auth_request_handler_reply(struct a
void auth_request_handler_reply_continue(struct auth_request *request,
const void *reply, size_t reply_size)
{
+ request->handler->reply_continue_callback(request, reply, reply_size);
+}
+
+static void
+auth_request_handler_default_reply_continue(struct auth_request *request,
+ const void *reply,
+ size_t reply_size)
+{
auth_request_handler_reply(request, AUTH_CLIENT_RESULT_CONTINUE,
reply, reply_size);
}
@@ -703,6 +729,7 @@ static void auth_str_append_userdb_extra
auth_str_add_keyvalue(dest, "master_user",
request->master_user);
}
+ auth_str_add_keyvalue(dest, "auth_mech", request->mech->mech_name);
if (*request->set->anonymous_username != '\0' &&
strcmp(request->user, request->set->anonymous_username) == 0) {
/* this is an anonymous login, either via ANONYMOUS
diff -up dovecot-2.3.8/src/auth/auth-request.h.CVE_2020_12674prereq dovecot-2.3.8/src/auth/auth-request.h
--- dovecot-2.3.8/src/auth/auth-request.h.CVE_2020_12674prereq 2019-10-08 10:46:18.000000000 +0200
+++ dovecot-2.3.8/src/auth/auth-request.h 2020-08-07 20:35:16.295684287 +0200
@@ -295,6 +295,8 @@ void auth_request_set_credentials(struct
set_credentials_callback_t *callback);
void auth_request_userdb_callback(enum userdb_result result,
struct auth_request *request);
+void auth_request_default_verify_plain_continue(struct auth_request *request,
+ verify_plain_callback_t *callback);
void auth_request_refresh_last_access(struct auth_request *request);
void auth_str_append(string_t *dest, const char *key, const char *value);

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 Epoch: 1
Version: 2.3.8 Version: 2.3.8
%global prever %{nil} %global prever %{nil}
Release: 3%{?dist} Release: 9%{?dist}
#dovecot itself is MIT, a few sources are PD, pigeonhole is LGPLv2 #dovecot itself is MIT, a few sources are PD, pigeonhole is LGPLv2
License: MIT and LGPLv2 License: MIT and LGPLv2
Group: System Environment/Daemons Group: System Environment/Daemons
@ -32,7 +32,6 @@ Patch6: dovecot-2.1.10-waitonline.patch
Patch8: dovecot-2.2.20-initbysystemd.patch Patch8: dovecot-2.2.20-initbysystemd.patch
Patch9: dovecot-2.2.22-systemd_w_protectsystem.patch Patch9: dovecot-2.2.22-systemd_w_protectsystem.patch
Patch10: dovecot-2.3.0.1-libxcrypt.patch
# sent upstream, rhbz#1630380 # sent upstream, rhbz#1630380
Patch11: dovecot-2.2.36-aclfix.patch Patch11: dovecot-2.2.36-aclfix.patch
@ -48,6 +47,36 @@ Patch14: dovecot-2.3.6-opensslhmac.patch
Patch15: dovecot-2.3.10-smtppre.patch Patch15: dovecot-2.3.10-smtppre.patch
Patch16: dovecot-2.3.10-CVE_2020_10957,10958,10967.patch Patch16: dovecot-2.3.10-CVE_2020_10957,10958,10967.patch
# from upstream, for dovecot <= 2.3.10.1
Patch17: dovecot-2.3.8-CVE_2020_12100prereq.patch
Patch18: dovecot-2.3.8-CVE_2020_12100.patch
Patch19: dovecot-2.3.8-CVE_2020_12100ph.patch
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 Source15: prestartscript
BuildRequires: openssl-devel, pam-devel, zlib-devel, bzip2-devel, libcap-devel BuildRequires: openssl-devel, pam-devel, zlib-devel, bzip2-devel, libcap-devel
@ -62,6 +91,7 @@ BuildRequires: krb5-devel
BuildRequires: quota-devel BuildRequires: quota-devel
BuildRequires: xz-devel BuildRequires: xz-devel
BuildRequires: lz4-devel BuildRequires: lz4-devel
BuildRequires: multilib-rpm-config
#BuildRequires: libsodium-devel #BuildRequires: libsodium-devel
#BuildRequires: libexttextcat-devel #BuildRequires: libexttextcat-devel
#BuildRequires: libstemmer-devel #BuildRequires: libstemmer-devel
@ -148,12 +178,35 @@ This package provides the development files for dovecot.
%patch6 -p1 -b .waitonline %patch6 -p1 -b .waitonline
%patch8 -p1 -b .initbysystemd %patch8 -p1 -b .initbysystemd
%patch9 -p1 -b .systemd_w_protectsystem %patch9 -p1 -b .systemd_w_protectsystem
#%patch10 -p1 -b .libxcrypt
%patch11 -p1 -b .aclfix %patch11 -p1 -b .aclfix
%patch13 -p1 -b .bigkey %patch13 -p1 -b .bigkey
%patch14 -p1 -b .opensslhmac %patch14 -p1 -b .opensslhmac
%patch15 -p1 -b .smtppre %patch15 -p1 -b .smtppre
%patch16 -p1 -b .CVE_2020_10957,10958,10967 %patch16 -p1 -b .CVE_2020_10957,10958,10967
%patch17 -p1 -b .CVE_2020_12100prereq
%patch18 -p1 -b .CVE_2020_12100
%patch19 -p1 -b .CVE_2020_12100ph
%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} pushd dovecot-2*3-pigeonhole-%{pigeonholever}
popd popd
@ -172,6 +225,7 @@ autoreconf -I . -fiv #required for aarch64 support
%endif %endif
%configure \ %configure \
INSTALL_DATA="install -c -p -m644" \ INSTALL_DATA="install -c -p -m644" \
--with-rundir=%{_rundir}/%{name} \
--docdir=%{_docdir}/%{name} \ --docdir=%{_docdir}/%{name} \
--disable-static \ --disable-static \
--disable-rpath \ --disable-rpath \
@ -218,9 +272,11 @@ rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$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 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} pushd dovecot-2*3-pigeonhole-%{pigeonholever}
make install DESTDIR=$RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT
@ -513,6 +569,27 @@ make check
%{_libdir}/%{name}/dict/libdriver_pgsql.so %{_libdir}/%{name}/dict/libdriver_pgsql.so
%changelog %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)
- fix CVE-2020-12674 crash due to assert in RPA implementation (#1866768)
* Mon Jun 01 2020 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-3 * Mon Jun 01 2020 Michal Hlavinka <mhlavink@redhat.com> - 1:2.3.8-3
- fix CVE-2020-10957 dovecot: malformed NOOP commands leads to DoS (#1840354) - fix CVE-2020-10957 dovecot: malformed NOOP commands leads to DoS (#1840354)
- fix CVE-2020-10958 dovecot: command followed by sufficient number of newlines - fix CVE-2020-10958 dovecot: command followed by sufficient number of newlines