Bug: lmtp proxy does not quote local parts with spaces

David Zambonini dovecot-e51 at deemzed.uk
Fri Nov 3 16:25:45 EET 2017


On 03/11/2017 11:48, Stephan Bosch wrote:
> Hi,
> 
> Sorry, we're in a bit of a v2.3 merge frenzy. Much of the LMTP code will be
> replaced in v2.3, but I'll give the  older code a look as well.
> 
> This can take a while though.

Thank you very much for getting back to me, I can appreciate it can get hectic,
and I don't wish to appear ungrateful, I wholeheartedly endorse/recommend
dovecot and the company I work for does use paid for OX elsewhere. For my own
part, the platform I manage is > 300,000 mailboxes and dovecot performs
incredibly well.

I came up with some much smaller patches that accomplish the same thing in v2.2
using built-in functions and pushing the re-encoding slightly further up the
call stack - address/username being interchangeable over most of the lmtp code
makes significant changes problematic, so I thought it best not to try a rework.

Looking at gitub, though, I don't see any significant changes in behaviour as
far as the problem I'm seeing goes, which is worrying.

What I'll do is leave the patches here for reference, and pick this up again
after the v2.3 release. If you do have time for a further response, I could also
provide them as pull requests against current on github if you'd like to request
that.

1. Cut on the final instead of initial @ when splitting user/domain parts in
LMTP, this can fix some issues where localpart contains a quoted @:

dovecot-2.2.33.2-reverse-domaincut.patch

2. Fully decode local part on receipt in LMTP, and re-encode when proxying. This
fixes the issue where quoted local quotes are stripped on proxy, preventing
successful proxying, and some director hashing problems (exposes
str_append_maybe_escape in message-address.h, some logging is still
inconsistent, though, but would require a major rework):

dovecot-2.2.33.2-quoted-local-proxy.patch

-- 
David Zambonini
-------------- next part --------------
--- dovecot-2.2.33.2/src/lib-mail/message-address.c	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.quoted-local-proxy/src/lib-mail/message-address.c	2017-11-02 12:21:57.572866755 +0000
@@ -34,7 +34,7 @@
 }
 
 /* quote with "" and escape all '\', '"' and "'" characters if need */
-static void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot)
+void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot)
 {
 	const char *p;
 
--- dovecot-2.2.33.2/src/lib-mail/message-address.h	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.quoted-local-proxy/src/lib-mail/message-address.h	2017-11-02 13:22:45.093866755 +0000
@@ -39,4 +39,7 @@
 				  const char *address, const char **username_r,
 				  const char **detail_r);
 
+/* quote with "" and escape all '\', '"' and "'" characters if need */
+void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot);
+
 #endif
--- dovecot-2.2.33.2/src/lmtp/commands.c	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.quoted-local-proxy/src/lmtp/commands.c	2017-11-02 13:50:25.794866755 +0000
@@ -441,34 +441,18 @@
 
 static const char *lmtp_unescape_address(const char *name)
 {
+	struct rfc822_parser_context parser;
 	string_t *str;
-	const char *p;
 
 	if (*name != '"')
 		return name;
 
-	/* quoted-string local-part. drop the quotes unless there's a
-	   '@' character inside or there's an error. */
+	/* decode quoted-string local-part */
 	str = t_str_new(128);
-	for (p = name+1; *p != '"'; p++) {
-		if (*p == '\0')
-			return name;
-		if (*p == '\\') {
-			if (p[1] == '\0') {
-				/* error */
-				return name;
-			}
-			p++;
-		}
-		if (*p == '@')
-			return name;
-		str_append_c(str, *p);
-	}
-	p++;
-	if (*p != '@' && *p != '\0')
-		return name;
+	rfc822_parser_init(&parser, (const unsigned char*)name, strlen(name), NULL);
+	rfc822_parse_quoted_string(&parser, str);
 
-	str_append(str, p);
+	str_append(str, (const char*)parser.data);
 	return str_c(str);
 }
 
--- dovecot-2.2.33.2/src/lmtp/lmtp-proxy.c	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.quoted-local-proxy/src/lmtp/lmtp-proxy.c	2017-11-02 13:49:41.154866755 +0000
@@ -8,6 +8,7 @@
 #include "ostream.h"
 #include "str.h"
 #include "time-util.h"
+#include "message-address.h"
 #include "lmtp-client.h"
 #include "lmtp-proxy.h"
 
@@ -288,6 +289,24 @@
 	lmtp_proxy_try_finish(conn->proxy);
 }
 
+static char *lmtp_proxy_escape_address(pool_t pool, const char *address) {
+	const char *domain;
+	string_t *dest;
+
+	domain = strrchr(address, '@');
+	dest = str_new(pool, 128);
+
+	if (domain == NULL) {
+		str_append_maybe_escape(dest, address, FALSE);
+	} else {
+		const char *local_part = t_strdup_until(address, domain);
+		str_append_maybe_escape(dest, local_part, FALSE);
+		str_append(dest, domain);
+	}
+
+	return str_free_without_data(&dest);
+}
+
 int lmtp_proxy_add_rcpt(struct lmtp_proxy *proxy, const char *address,
 			const struct lmtp_proxy_rcpt_settings *set)
 {
@@ -301,10 +320,10 @@
 	rcpt = p_new(proxy->pool, struct lmtp_proxy_recipient, 1);
 	rcpt->idx = array_count(&proxy->rcpt_to);
 	rcpt->conn = conn;
-	rcpt->address = p_strdup(proxy->pool, address);
+	rcpt->address = lmtp_proxy_escape_address(proxy->pool, address);
 	array_append(&proxy->rcpt_to, &rcpt, 1);
 
-	lmtp_client_add_rcpt_params(conn->client, address, &set->params,
+	lmtp_client_add_rcpt_params(conn->client, rcpt->address, &set->params,
 				    lmtp_proxy_conn_rcpt_to,
 				    lmtp_proxy_conn_data, rcpt);
 	return 0;
-------------- next part --------------
--- dovecot-2.2.33.2/src/lib-mail/mail-user-hash.c	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.reverse-domaincut/src/lib-mail/mail-user-hash.c	2017-11-02 16:04:03.724866755 +0000
@@ -33,9 +33,13 @@
 		tab = t_malloc(sizeof(static_tab));
 		memcpy(tab, static_tab, sizeof(static_tab));
 		tab[0].value = username;
-		tab[1].value = t_strcut(username, '@');
-		tab[2].value = strchr(username, '@');
-		if (tab[2].value != NULL) tab[2].value++;
+		tab[2].value = strrchr(username, '@');
+		if (tab[2].value != NULL) {
+			tab[1].value = t_strdup_until(username, tab[2].value);
+			tab[2].value++;
+		} else {
+			tab[1].value = username;
+		}
 
 		var_expand(str, format, tab);
 		md5_get_digest(str_data(str), str_len(str), md5);
--- dovecot-2.2.33.2/src/lib-mail/message-address.c	2017-10-05 18:10:44.000000000 +0100
+++ dovecot-2.2.33.2.reverse-domaincut/src/lib-mail/message-address.c	2017-11-02 16:00:30.926866755 +0000
@@ -540,7 +540,7 @@
 	if (*delimiter_string == '\0')
 		return;
 
-	domain = strchr(address, '@');
+	domain = strrchr(address, '@');
 	p = strstr(address, delimiter_string);
 	if (p != NULL && (domain == NULL || p < domain)) {
 		/* user+detail at domain */


More information about the dovecot mailing list