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: root@mx3.example
Received: from foobar (localhost [::1])
        by mx3.example (OpenSMTPD) with SMTP id 6af41037
        for <root@mx3.example>;
        Mon, 27 Nov 2023 09:59:16 -0300 (-03)
        this will be appended :-/
Date: now
Subject: header folding
To: root@mx3.example
From: Crystal@mx3.example

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

Reply via email to