On Fri, Oct 04, 2019 at 08:43:28AM +0200, Martijn van Duren wrote:
> >> annoying bumps will just be moved around. If we *really* want to fix
> >> this we need to make it fit within the specifications:
> >>
> >> [...]
> >>
> >> This means stop opportunistic scanning for '\r' in iobuf!
> >>
> > 
> > Sure but fixing iobuf is not a two liner and it affects virtually all of
> > the daemon and at this point we're looking for stability in the code, so
> > unless eric@ or you can come up with a diff that's trivial and that will
> > not affect any code paths beyond smtp client and filter getlines(), I'll
> > prefer a degraded margin case than taking the risk of a regression.
> > 
> > I don't know iobuf enough to dive into this in a rush but if you managed
> > to pull a iobuf_getline_lf() that's fully isolated from rest of iobuf so
> > we could use that one in smtp_session.c and lka_proc.c, I'd be fine too,
> > and it would solve your concern.
> > 
> > 
> This whole ordeal was originally triggered by filter-dkimsign returning
> invalid signatures because additional \r were stripped. This is an
> uncommon usecase.
> The "fix" at the time was disallowing random '\r', which seemed to work
> for some time, but now it turns out that it breaks semarie's printer.
> 
> If we don't want to rush anything and keep as many systems running as
> possible in a similar way we did before I propose we just revert the
> '\r' forbidden diff and accept that signatures are broken for clients
> that don't send valid mails until we fix this in a proper way.
> 
> Doing it this way will keep things best effort running with minimal
> code that could cause confusion once we start fixing this.
> 
> Index: smtp_session.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/smtpd/smtp_session.c,v
> retrieving revision 1.414
> diff -u -p -r1.414 smtp_session.c
> --- smtp_session.c    3 Oct 2019 05:08:21 -0000       1.414
> +++ smtp_session.c    4 Oct 2019 06:41:58 -0000
> @@ -1109,15 +1109,6 @@ smtp_io(struct io *io, int evt, void *ar
>               if (line == NULL)
>                       return;
>  
> -             if (strchr(line, '\r')) {
> -                     s->flags |= SF_BADINPUT;
> -                     smtp_reply(s, "500 %s <CR> is only allowed before <LF>",
> -                         esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
> -                     smtp_enter_state(s, STATE_QUIT);
> -                     io_set_write(io);
> -                     return;
> -             }
> -
>               /* Message body */
>               eom = 0;
>               if (s->state == STATE_BODY) {


I'm fine with that.

Alternatively, I took a look at what it would take to write an
iobuf_getline_nostrip() function which would be called _only_ in the
filtering code path.

The diff is not too invasive:

- introduce a iobuf_getline_nostrip() function so we don't touch the
  iobuf_getline() function (it's used in the MTA layer too and we're
  not getting anywhere close that), we can factor post release.

- the client input is read with iobuf_getline() which strips \r, but
  the indirection through filters use iobuf_getline_nostrip(), which
  will let the '\r', effectively stopping opportunistic strip as you
  suggest.

Setup that don't have filters will go _exactly_ through old behavior
whereas setups with filters will have all \r preserved, keeping DKIM
ok.

I'm running with that diff right now.

I'm ok with either one of committing a degraded DKIM for trailing-\r
or going towards that diff.

comments ?


diff --git a/smtpd/iobuf.c b/smtpd/iobuf.c
index f5d8b20a..78a83cc6 100644
--- a/smtpd/iobuf.c
+++ b/smtpd/iobuf.c
@@ -184,6 +184,34 @@ iobuf_getline(struct iobuf *iobuf, size_t *rlen)
        return (NULL);
 }
 
+char *
+iobuf_getline_nostrip(struct iobuf *iobuf, size_t *rlen)
+{
+       char    *buf;
+       size_t   len, i;
+
+       buf = iobuf_data(iobuf);
+       len = iobuf_len(iobuf);
+
+       for (i = 0; i + 1 <= len; i++)
+               if (buf[i] == '\n') {
+                       /* Note: the returned address points into the iobuf
+                        * buffer.  We NUL-end it for convenience, and discard
+                        * the data from the iobuf, so that the caller doesn't
+                        * have to do it.  The data remains "valid" as long
+                        * as the iobuf does not overwrite it, that is until
+                        * the next call to iobuf_normalize() or iobuf_extend().
+                        */
+                       iobuf_drop(iobuf, i + 1);
+                       buf[i] = '\0';
+                       if (rlen)
+                               *rlen = i;
+                       return (buf);
+               }
+
+       return (NULL);
+}
+
 void
 iobuf_normalize(struct iobuf *io)
 {
diff --git a/smtpd/iobuf.h b/smtpd/iobuf.h
index c454d0a1..e6e8023c 100644
--- a/smtpd/iobuf.h
+++ b/smtpd/iobuf.h
@@ -52,6 +52,7 @@ size_t        iobuf_len(struct iobuf *);
 size_t iobuf_left(struct iobuf *);
 char   *iobuf_data(struct iobuf *);
 char   *iobuf_getline(struct iobuf *, size_t *);
+char   *iobuf_getline_nostrip(struct iobuf *, size_t *);
 ssize_t        iobuf_read(struct iobuf *, int);
 ssize_t        iobuf_read_tls(struct iobuf *, void *);
 
diff --git a/smtpd/ioev.c b/smtpd/ioev.c
index d36210fd..4b3733de 100644
--- a/smtpd/ioev.c
+++ b/smtpd/ioev.c
@@ -506,6 +506,12 @@ io_getline(struct io *io, size_t *sz)
        return iobuf_getline(&io->iobuf, sz);
 }
 
+char *
+io_getline_nostrip(struct io *io, size_t *sz)
+{
+       return iobuf_getline_nostrip(&io->iobuf, sz);
+}
+
 void
 io_drop(struct io *io, size_t sz)
 {
diff --git a/smtpd/ioev.h b/smtpd/ioev.h
index f155a7fc..857f7a20 100644
--- a/smtpd/ioev.h
+++ b/smtpd/ioev.h
@@ -67,4 +67,5 @@ size_t io_queued(struct io *);
 void* io_data(struct io *);
 size_t io_datalen(struct io *);
 char* io_getline(struct io *, size_t *);
+char* io_getline_nostrip(struct io *, size_t *);
 void io_drop(struct io *, size_t);
diff --git a/smtpd/lka_filter.c b/smtpd/lka_filter.c
index 695ce4c3..1a7c6c39 100644
--- a/smtpd/lka_filter.c
+++ b/smtpd/lka_filter.c
@@ -399,7 +399,7 @@ filter_session_io(struct io *io, int evt, void *arg)
        switch (evt) {
        case IO_DATAIN:
        nextline:
-               line = io_getline(fs->io, &len);
+               line = io_getline_nostrip(fs->io, &len);
                /* No complete line received */
                if (line == NULL)
                        return;
@@ -653,7 +653,7 @@ filter_data_internal(struct filter_session *fs, uint64_t 
token, uint64_t reqid,
 
        /* no filter_entry, we either had none or reached end of chain */
        if (filter_entry == NULL) {
-               io_printf(fs->io, "%s\r\n", line);
+               io_printf(fs->io, "%s\n", line);
                return;
        }
 
diff --git a/smtpd/lka_proc.c b/smtpd/lka_proc.c
index f90d006f..c44a081f 100644
--- a/smtpd/lka_proc.c
+++ b/smtpd/lka_proc.c
@@ -152,7 +152,7 @@ processor_io(struct io *io, int evt, void *arg)
 
        switch (evt) {
        case IO_DATAIN:
-               while ((line = io_getline(io, &len)) != NULL) {
+               while ((line = io_getline_nostrip(io, &len)) != NULL) {
                        if (strncmp("register|", line, 9) == 0) {
                                processor_register(name, line);
                                continue;
@@ -182,7 +182,7 @@ processor_errfd(struct io *io, int evt, void *arg)
 
        switch (evt) {
        case IO_DATAIN:
-               while ((line = io_getline(io, &len)) != NULL)
+               while ((line = io_getline_nostrip(io, &len)) != NULL)
                        log_warnx("%s: %s", name, line);
        }
 }
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index 95db9099..2e38f5f7 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -1109,15 +1109,6 @@ smtp_io(struct io *io, int evt, void *arg)
                if (line == NULL)
                        return;
 
-               if (strchr(line, '\r')) {
-                       s->flags |= SF_BADINPUT;
-                       smtp_reply(s, "500 %s <CR> is only allowed before <LF>",
-                           esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
-                       smtp_enter_state(s, STATE_QUIT);
-                       io_set_write(io);
-                       return;
-               }
-
                /* Message body */
                eom = 0;
                if (s->state == STATE_BODY) {
@@ -2792,7 +2783,7 @@ filter_session_io(struct io *io, int evt, void *arg)
        switch (evt) {
        case IO_DATAIN:
        nextline:
-               line = io_getline(tx->filter, &len);
+               line = io_getline_nostrip(tx->filter, &len);
                /* No complete line received */
                if (line == NULL)
                        return;




-- 
Gilles Chehade                                                 @poolpOrg

https://www.poolp.org            patreon: https://www.patreon.com/gilles

Reply via email to