Hi Frank,

Op 30-05-2022 om 18:30 schreef opensmtpd.open...@xpoundit.com:
Hi Harri,

we had issues with e-mails containing ORCPT as well and fixed the
rejection with a patch. Originally, we created the patch when 6.7 was
out and applied it to the version of OpenSMTPD available in the FreeBSD
ports.

As of today, OpenSMTPD 6.8 is available in the FreeBSD ports system.
The attached patches can be applied to this version (if you are on
FreeBSD, just put them into ports/mail/opensmtpd/files).

If needed, I can massage the patches so they can be applied against the
OpenBSD base as well, where OpenSMTPD resides in nowadays (we mainly
use it on FreeBSD). I did not do this yet, since I wanted to provide a
quick answer.

In our case, the above mentioned groupware introduced characters in the
ORCPT field (colons.., 0x3a), that led smtp_tx_rcpt_to()
(usr.sbin/smtpd/smtp_session.c) to return with "553 ORCPT address
syntax error".

RFC3461 led us to the solution we are using today.

In section 4.2 [1], the ABNF of ORCPT is defined as:

orcpt-parameter = "ORCPT=" original-recipient-address
       original-recipient-address = addr-type ";" xtext
       addr-type = atom

The log you see is:

May 27 08:42:30 mymta smtpd[10310]: f06a752b657b4a05 smtp failed-
command command="RCPT TO:<u...@example.com>
ORCPT=rfc822;u...@example.com" result="550 Invalid recipient:
<u...@example.com>"

According to the ABNF of ORCPT, everything after addr-type ";"
("rfc822;" in your case) is supposed to be xtext, which is described a
bit earlier in the introductory part of section 4 [2].

xtext = *( xchar / hexchar )

xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive,
         except for "+" and "=".

; "hexchar"s are intended to encode octets that cannot appear
; as ASCII characters within an esmtp-value.

hexchar = ASCII "+" immediately followed by two upper case
           hexadecimal digits

smtp_tx_rcpt_to() in usr.sbin/smtpd/smtp_session.c tries to convert the
text of the ORCPT DSN into an e-mail address and wants to check the
validity of the local and the domain part. We replaced this check, are
validating the xtext portion as specified above and are replying with a
more precise error message if this check fails:

  if (strncasecmp(opt, "rfc822;", 7) == 0)
          opt += 7;

-if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) ||
-    !valid_localpart(tx->evp.dsn_orcpt.user) ||
-    (strlen(tx->evp.dsn_orcpt.domain) != 0 &&
-     !valid_domainpart(tx->evp.dsn_orcpt.domain))) {
+if (!valid_xtext(opt)) {
          smtp_reply(tx->session,
-            "553 ORCPT address syntax error");
+            "553 ORCPT xtext syntax error");
          return;
  }


In usr.sbin/smtpd/util.c we added valid_xtext():

+int
+valid_xtext(const char *s)
+{
+        while (*s != '\0') {
+                if(*s == '\x2b' || *s == '\x3d') {
+                        return 0;
+                } else if(*s <= '\x21' || *s >= '\x7e') {
+                        return 0;
+                } else {
+                        s++;
+                        continue;
+                }
+                return 0;
+        }
+        return 1;
+}

I hope this helps to narrow down your issue a bit. What kind of non-e-
mailish characters do you see in the ORCPT?

In our case, the xtext portion of the ORCPT quite often contained valid
e-mail addresses, but sometimes, it did not. As far as we understood
the RFCs, xtext doas not necessarily need to be an e-mail address. This
is why we decided to replace the original check. The above mentioned
groupware used colons as field separators inside the xtext portion to
keep track of the communication belonging to certain thread or, well,
recipients.

What do the others think of the way we are handling the ORCPT?

Cheers

Frank



[1] https://datatracker.ietf.org/doc/html/rfc3461#section-4.2
[2] https://datatracker.ietf.org/doc/html/rfc3461#section-4




On Mon, 2022-05-30 at 09:04 +0200, Harald Dunkel wrote:
Hi folks,

my MTA (opensmtpd on OpenBSD 7.0) rejects a few EMails with
a message like

May 27 08:42:30 mymta smtpd[10310]: f06a752b657b4a05 smtp failed-
command command="RCPT TO:<u...@example.com>
ORCPT=rfc822;u...@example.com" result="550 Invalid recipient:
<u...@example.com>"

in /var/log/maillog. The EMails to u...@example.com without
"ORCPT=rfc822;" are accepted and forwarded as usual. All EMails
with "ORCPT=rfc822;" to any user are rejected.

I have found https://github.com/OpenSMTPD/OpenSMTPD/issues/658
of course, but I don't see a configuration issue on my side.
My MTA is just an MTA, there are no local users except for root.

Every insightful comment is highly appreciated

Harri



I've also been struck by this problem over the years when some enterprise users tried to mail an opensmtpd server (I'm talking about the ORCPT problem, not the more generic "550 Invalid recipient" problem from Harri).

I have altered your patch a bit:
* rebase on OpenBSD current
* set the dsn in case the orcpt can be parsed successfully as an address
* support hexchars
* support ! and ~ (RFC: "!" (33) and "~" (126) inclusive)
* check for max length of 500 characters as stated in the RFC

Also, in the RFC it is stated that the ORCPT must match with the xtext encoded version of RCPT:

   When initially submitting a message via SMTP, if the ORCPT parameter
   is used, it MUST contain the same address as the RCPT TO address
   (unlike the RCPT TO address, the ORCPT parameter will be encoded as
   xtext).

Would this not imply that invalid e-mail addresses are invalid?

I thought the cleanest approach would be to just decode ORCPT as xtext before parsing the address, but since you have seen invalid ORCPT e-mail addresses in the wild, I wonder if we really want to error on this?

I'm also trying to catch some more use-cases of ORCPT from production.

See attached patch mail where processing continues without DSN when orcpt is valid xtext, but not a valid address. This is a middle ground between your patch and the current version of OpenSMTPD.

-Tim
From f176a73f033edd022e7df77ddf0008bd37ff693c Mon Sep 17 00:00:00 2001
From: tim <t...@cypher.netsend.nl>
Date: Wed, 3 Aug 2022 17:24:01 +0200
Subject: [PATCH] ignore orcpt if not a valid e-mail address

---
 usr.sbin/smtpd/smtp_session.c | 32 ++++++++++++++++++++++++++------
 usr.sbin/smtpd/smtpd.h        |  1 +
 usr.sbin/smtpd/util.c         | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+), 6 deletions(-)

diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index 13756932208..ee21f8ecafc 100644
--- a/usr.sbin/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -35,6 +35,7 @@
 #define	SMTP_LINE_MAX			65535
 #define	DATA_HIWAT			65535
 #define	APPEND_DOMAIN_BUFFER_SIZE	SMTP_LINE_MAX
+#define	ORCPT_MAX			500	/* RFC 3461 4.2 */
 
 enum smtp_state {
 	STATE_NEW = 0,
@@ -2415,6 +2416,7 @@ smtp_tx_create_message(struct smtp_tx *tx)
 static void
 smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
 {
+	struct mailaddr orcptaddr;
 	char *opt, *p;
 	char *copy;
 	char tmp[SMTP_LINE_MAX];
@@ -2464,17 +2466,35 @@ smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
 				return;
 			}
 		} else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) {
+			if (strnlen(opt, ORCPT_MAX + 1) > ORCPT_MAX) {
+				smtp_reply(tx->session,
+				    "553 ORCPT parameter too long");
+				return;
+			}
+
 			opt += 6;
 
 			if (strncasecmp(opt, "rfc822;", 7) == 0)
 				opt += 7;
 
-			if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) ||
-			    !valid_localpart(tx->evp.dsn_orcpt.user) ||
-			    (strlen(tx->evp.dsn_orcpt.domain) != 0 &&
-			     !valid_domainpart(tx->evp.dsn_orcpt.domain))) {
-				smtp_reply(tx->session,
-				    "553 ORCPT address syntax error");
+			if (!valid_xtext(opt)) {
+				log_info("%016"PRIx64" invalid ORCPT xtext %s",
+				    tx->session->id, opt);
+				smtp_reply(tx->session, "553 ORCPT xtext syntax error");
+				return;
+			}
+
+			if (text_to_mailaddr(&orcptaddr, opt) &&
+			    valid_localpart(orcptaddr.user) &&
+			    (strlen(orcptaddr.domain) != 0 &&
+			     valid_domainpart(orcptaddr.domain))) {
+				log_info("%016"PRIx64" valid ORCPT address %s",
+				    tx->session->id, opt);
+				tx->evp.dsn_orcpt = orcptaddr;
+			} else {
+				log_info("%016"PRIx64" invalid ORCPT address %s",
+				    tx->session->id, opt);
+				smtp_reply(tx->session, "553 ORCPT address syntax error");
 				return;
 			}
 		} else {
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 125a6a5dfbe..c59706885e2 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1702,6 +1702,7 @@ int mailaddr_match(const struct mailaddr *, const struct mailaddr *);
 int valid_localpart(const char *);
 int valid_domainpart(const char *);
 int valid_domainname(const char *);
+int valid_xtext(const char *s);
 int valid_smtp_response(const char *);
 int secure_file(int, char *, char *, uid_t, int);
 int  lowercase(char *, const char *, size_t);
diff --git a/usr.sbin/smtpd/util.c b/usr.sbin/smtpd/util.c
index feb663cc61e..0c4d0015fa4 100644
--- a/usr.sbin/smtpd/util.c
+++ b/usr.sbin/smtpd/util.c
@@ -515,6 +515,38 @@ valid_domainname(const char *str)
 	return 1;
 }
 
+int
+valid_xtext(const char *s)
+{
+	while (*s != '\0') {
+		if (*s == '=')
+			return 0;
+
+		if (*s < '\x21' || *s > '\x7e')
+			return 0;
+
+		if (*s == '+') {
+			/* expect hexchar "+XX" RFC 3461 4. */
+			if (strnlen(s, 3) != 3)
+				return 0;
+
+			s++;
+
+			if (!isdigit(*s) && !isupper(*s))
+				return 0;
+
+			s++;
+
+			if (!isdigit(*s) && !isupper(*s))
+				return 0;
+		}
+
+		s++;
+	}
+
+	return 1;
+}
+
 int
 valid_smtp_response(const char *s)
 {
-- 
2.35.1

Reply via email to