Hi Wietse,

[ just subscribed to the list, I realized that our past conversation
  was dropped since I was not subscribed, never mind ]

On Thu, Jun 07, 2012 at 08:16:53PM -0400, Wietse Venema wrote:
> Willy Tarreau:
> > > >Regardless of command format details, if the proxy prepends a command
> > > >to the client's SMTP stream, then postscreen must use unbuffered
> > > >I/O to read that command. If buffering were turned on, the buffering
> > > >layer could read past the proxy's<CR><LF>  and eat up part of the
> > > >client input kind-of like CVE-2011-0411.
> > 
> > Precisely on this point there is an easier way, it consists in using
> > recv(MSG_PEEK). The big advantage is that you don't need to store the
> > temporary bytes you've read since they remain in the kernel's buffers.
> > So it more or less looks like this :
> > 
> >      len = recv(fd, trash, sizeof(trash), MSG_PEEK);
> >      if (len == -1 && errno == EAGAIN)
> >            return;
> > 
> >      lf = memchr(trash, '\n', len);
> >      if (lf == NULL) {
> >            if (len < trash) /* Huh?? */
> >                return;
> >            /* else abort the connection */
> >      }
> 
> In an event-driven program such as postscreen, this code breaks
> when the proxy line arrives as multiple fragments.
>
> If the program does not drain proxy line fragments from the kernel
> buffer, then the socket remains readable and the program will go
> into a read-notification loop until the entire line is received.

Indeed this is totally true. I would say that given the short size
of the message the risk of this occurring is barely 0 explaining
whit this has probably never hit anybody, but it would be sufficient
that someone implements the protocol using a series of
write(fd, buf++, 1) and you'd be spinning (even worse if it dies in
the middle).

I've seen implementations doing recv(MSG_PEEK) and reject connections
from incomplete messages (again, almost zero risk but YMMV).

In haproxy, I'm using the input buffer associated to the fd, so I
don't have this problem. I'm basically doing a recv() on the buffer.
I think it is equivalent to the VSTREAM you're using.

In postscreen you don't want to do this since you don't want to
consume any possible incoming data (btw you probably drop the
connection if you get any data at this point). That said, if you
have a buffer associated to the connection, then you can perform
the first MSG_PEEK to check the pending data size and then a real
recv() to only consume up to the end of line.

But then doing so indeed invalidates the following suggestion.

> This implies that the following suggestion is not valid for an
> event-driven program such as postscreen:
> 
> > On the one hand, if it is as trivial to make smtpd parse the PROXY
> > line as it was for postscreen, it can solve the problem by having
> > postscreen not consume the first line, which makes sense in that
> > postscreen remains the first layer analyser which doesn't mangle
> > data on the connection.


> Either you need to update the protocol spec (require non-fragmented
> proxy lines)

I have mixed opinions on this. On the one hand, we can't really impose
lower layers segmentation behaviour, so from a layering perspective, it
is not correct. On the other hand, the use cases for the protocol are
very specific. We're the very first segment over the connection so we
are always allowed to send at least one MSS. Nobody should sanely use
this proxy line on connections with an MSS lower than the 116 bytes a
max line may be for long IPv6 addresses and ports.

So indeed, I'm tempted to follow your suggestion, it will ease processing
for everyone and ensure that nobody tries sending fragmented lines. We'd
rely on a sane lower layer and declare other cases out of scope.

> or provide a code example that doesn't go into a
> read-notification loop when the proxy line arrives as multiple
> fragments.

With a buffer this problem does not happen, but it's the first case I'm
facing this need with fd passing, which makes me scratch my head a lot.
I really like the way you're plugging postscreen in front of smtpd, and
I'd like to ensure we don't make it complex to keep this nice model.

That's why I think that adding a sane requirement in the spec should be
the most adequate solution. If in the mean time you get a smarter idea,
do not hesitate to share it :-)

I'll keep thinking about it a bit before updating it. I think I will also
propose some generic code in the spec for both sides.

Best regards,
Willy

Reply via email to