[stunnel-users] Default passthrough to different destination?

Peter Pentchev roam at ringlet.net
Fri Feb 3 11:33:53 CET 2017

On Fri, Feb 03, 2017 at 09:47:10AM +0000, Mark Boyce wrote:
[formatting fixed; top-posting is not the best way to carry out a conversation]
> > On 3 Feb 2017, at 08:40, Peter Pentchev <roam at ringlet.net> wrote:
> > 
> > On Thu, Feb 02, 2017 at 09:54:38PM +0000, Mark Boyce wrote:
> >> Hi All
> >> 
> >> Wondering if there’s a way to pass an unencrypted connections traffic to
> >> an alternative location if a client does not SSL/TLS with the stunnel
> >> server?
> >> 
> >> So considering stunnel running as a server to wrap an unencrypted SMTP
> >> server.  If the SMTP client/server talks SSL/TLS all is good and as
> >> expected.  If the client tries to talk without encryption it gets
> >> disconnect. 
> >> 
> >> Is there any way to send this traffic elsewhere rather than
> >> disconnecting the client?  So that stunnel is adding an SSL/TLS option
> >> to a service rather than enforcing it. Splitting the traffic to
> >> destination servers based on if the client was encrypted or not.
> > 
> > stunnel itself cannot do this; one might write a trivial wrapper to
> > do it, but I believe that there might be a larger problem here.
> > 
> > You mention SMTP.  Doesn't the SMTP protocol *require* the server to
> > send its banner (220 Hi there, I'm an SMTP server, who are you?) before
> > the client sends its first command?  I think that there are servers
> > that actually enforce this requirement for spam control - some spambots
> > are dumb enough to just open a TCP connection and blast a series of
> > SMTP commands without waiting for the server's greeting (to save on
> > round-trip times and such), and some servers deliberately delay their
> > 220 greeting for a little while and immediately reject the connection
> > if the client tries to talk to them before that.
> > 
> > So, um, how does the redirector know whether this is an SSL/TLS client
> > or not if the server has to send its greeting first? :)  Of course, one
> > could do something like "wait for a second or two, see if the client
> > starts an SSL/TLS session; if not, pass it on to the unencrypted server
> > thing", but this will fail badly if the connection has a really high
> > latency or the client machine is badly overloaded so that it doesn't
> > send its SSL/TLS Client Hello in time, and it would also enforce
> > an additional delay on *every* unencrypted connection.
> Hi Peter & all
> I’ve only been looking at stunnnel a short while but looking at a simple
> server setup it appears to do the following;
> Scenario 1
> Client connects on SSL - stream is forwarded as unencrypted
> Scenario 2
> Client connects unencrypted and does / offers STARTTLS - stream is
> forwarded as unencrypted
> Scenario 3
> Client connects unencrypted and does NOT do or offer a STARTTLS - server
> disconnects
> Scenario 1 & 2 work with stunnel as intended.  What I’m looking to do is
> make scenario 3 connect to an alternative location rather than
> disconnect.

Oh, I did not realize that you were using stunnel with "protocol smtp".
This does change things a bit :)

OK, before going into details, let me first ask something: why are you
actually doing this?  What exactly is the setup that you're trying to
use stunnel in?

- you have an SMTP server that supposedly does not understand STARTTLS;
  what kind of server is this, and do you really need to use it instead
  of another implementation?

- you have an SMTP client that knows how to use STARTTLS; good!

- you have another SMTP client that does not know how to use STARTTLS;
  do you really need to support that?  Is it a mail user agent trying to
  send a message out into the world through a local SMTP forwarder, or
  is it a random SMTP server from the Internet trying to have a message
  delivered to your server?  Well, if it's the latter, then yeah, there
  might still be servers out there that don't necessarily do STARTTLS.

So I guess it all boils down to "are you really sure that you cannot run
an SMTP server that handles STARTTLS by itself?"

> So looking at SMTP the disconnection seems to be when a client connects
> unencrypted and does a helo/ehelo which doesn’t end in STARTTLS.
> What I’ve not managed to find yet is the point in the code where stunnel
> establishes the connection to the real server and if that is already
> connected by the time stunnel makes its disconnection decision.

OK, if you're interested in the stunnel code, this interaction happens
in the protocol() and smtp_server() functions in src/protocol.c, invoked
by the client_try() function in src/client.c.

If you're interested in how things actually happen:
- the SMTP client connects to the stunnel server
- stunnel immediately establishes an unencrypted connection to the real
  SMTP server so that it may forward the greeting (see below for the
  EHLO options discussion)
- stunnel waits a little bit to check if the client will actually start
  a TLS session... erm, yeah, it seems that stunnel actually does what
  I wrote above might fail on high-latency connections; apparently this
  doesn't happen so often in the real world :)
- if the client does send anything, the smtp_server() function assumes
  that this "anything" is a TLS Client Hello message, skips the STARTTLS
  negotiation altogether and lets stunnel do its TLS negotiation and
  normal forwarding
- if the client does NOT send anything, smtp_server() assumes that this
  is an SMTP client that is really trying to establish an unencrypted
  connection first and then send STARTTLS.  Now stunnel reads the SMTP
  server's greeting, replaces the first line a little bit to announce
  its own STARTTLS capability, and sends it to the client.
- the client now sends its own EHLO, stunnel swallows it, ignoring its
  actual contents (no need to forward it to the server, the client will
  send another EHLO after the TLS session is established)
- the client now sends the STARTTLS command... or if it doesn't, stunnel
  aborts the connection
- if the client does indeed send a STARTTLS command, smtp_server() says
  "great, everything is fine now, let's go to the actual TLS negotiation
  phase!" and stunnel goes into its real work mode - negotiate TLS, then
  forward between the encrypted and the unencrypted connections

So...  What you want to do is modify stunnel's behavior in the
next-to-last point, the "if it doesn't, stunnel aborts the connection"
step.  Well, as I said before, stunnel itself cannot do that - it's
written to always forward between an encrypted and an unencrypted
connection.  It might be possible to write another wrapper, a program
that listens in front of stunnel and does something like:

- wait for a client connection
- establish a connection to stunnel
- wait for either the client or stunnel to say something
- if the first thing received from the client is a TLS Client Hello
  message, just forward everything between the two
- if stunnel says something first, read its 220 greeting (the one that
  stunnel, in its turn, forwarded from the real SMTP server behint it),
  and echo it to the client
- read the client's EHLO, forward it to stunnel, but also remember it
- read stunnel's "250 STARTTLS" response, forward it to the client
- read the client's next command...
  - if it is a STARTTLS command, forward it to stunnel and then just
    keep forwarding everything between the two
  - if it is not a STARTTLS command, then close the connection to
    stunnel and, well, here comes the tricky part:
    - establish a new connection to a real SMTP server (either the same
      one that sits behind stunnel or another one)
    - wait for the server's greeting
    - send the remembered client EHLO to the server
    - wait for the server's "250 OK" response
    - send the client's command (the one that was not STARTTLS) to
      the server
    - just keep forwarding everything between the two

Now, there is at least one problem with that: if the server that you're
forwarding to is not the same as the one that stunnel will forward the
encrypted connection to, then the two servers may have different
capabilities announced in their 220 banners, and the client's EHLO as
sent to the server behind stunnel might not be the same EHLO that the
client would've sent to the other server.  Even worse, the server behind
stunnel might have advertised some SMTP options that the other server
may not support, and the client will try to use them with potentially
disastrous consequences (most probably failed attempts to send a
message, but also possibly "almost successful" attempts to send
*mangled* messages, which would be way worse).

In theory, I might be able to write such a forwarder in a couple of
days; however, I cannot give you any guarantees as to exactly when it'll
be ready, and there remains the problem of the mismatched capabilities
if the two real SMTP servers are different.


Peter Pentchev  roam at ringlet.net roam at FreeBSD.org pp at storpool.com
PGP key:        http://people.FreeBSD.org/~roam/roam.key.asc
Key fingerprint 2EE7 A7A5 17FC 124C F115  C354 651E EFB0 2527 DF13
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://www.stunnel.org/pipermail/stunnel-users/attachments/20170203/334d0900/attachment.sig>

More information about the stunnel-users mailing list