Technically this might not be a bug, but it certainly allows any user to cause
unexpected behaviour so I think it should be fixed...
It's possible to use header folding to append arbitrary data to the final
'Received:' header that smtpd generates, as smtpd does not reject an initial
data line which begins with a space or a tab.
Here is an example:
# telnet ::1 25
Trying ::1...
Connected to ::1.
Escape character is '^]'.
220 mx3.example ESMTP OpenSMTPD
helo foobar
250 mx3.example Hello foobar [::1], pleased to meet you
mail from: <>
250 2.0.0 Ok
rcpt to: <root>
250 2.1.5 Destination address valid: Recipient ok
data
354 Enter mail, end with "." on a line by itself
this will be appended :-/
Date: now
Subject: header folding
To: root
From: Crystal
Looks like a bug to me
.
250 2.0.0 6af41037 Message accepted for delivery
quit
221 2.0.0 Bye
Connection closed by foreign host.
#
The headers are written to the mailbox as:
Delivered-To: [email protected]
Received: from foobar (localhost [::1])
by mx3.example (OpenSMTPD) with SMTP id 6af41037
for <[email protected]>;
Mon, 27 Nov 2023 09:59:16 -0300 (-03)
this will be appended :-/
Date: now
Subject: header folding
To: [email protected]
From: [email protected]
I don't think that this is exactly exploitable in any way, but you could
certainly append a different date to the last Received: header, which might
cause software processing it to do something unexpected.
The following patch causes smtpd to error out if a folding-continuation line
is submitted as the first line of the mail.
--- smtp_session.c.dist Mon Nov 27 09:58:26 2023
+++ smtp_session.c Mon Nov 27 11:50:18 2023
@@ -58,6 +58,7 @@
SF_BOUNCE = 0x0010,
SF_VERIFIED = 0x0020,
SF_BADINPUT = 0x0080,
+ SF_FOK = 0x0100,
};
enum {
@@ -1140,6 +1141,20 @@
if (s->tx->datain > env->sc_maxsize)
s->tx->error = TX_ERROR_SIZE;
}
+
+ /* Check for header folding at start of input */
+ if (!((s)->flags & SF_FOK) && (line[0]==' ' ||
+ line[0]=='\t')) {
+ smtp_reply(s, "500 %s Header folding invalid "
+ "here", esc_code(ESC_STATUS_PERMFAIL,
+ ESC_OTHER_STATUS));
+ smtp_enter_state(s, STATE_QUIT);
+ io_set_write(io);
+ return ;
+ }
+
+ (s)->flags |= SF_FOK;
+
eom = (s->tx->filter == NULL) ?
smtp_tx_dataline(s->tx, line) :
smtp_tx_filtered_dataline(s->tx, line);
@@ -1332,6 +1347,7 @@
if (!smtp_check_data(s, args))
break;
smtp_filter_phase(FILTER_DATA, s, NULL);
+ s->flags &= ~SF_FOK;
break;
/*