Thursday, April 9, 2015

Sending Mail Via SMTP Over Implicit SSL in .Net

My Kingdom for Port 25

I recently ran across a situation where I wanted my home workstation to send emails on a periodic basis. No problem, I'm a developer, so I'll just whip up a quick .Net console application and set it to run as a scheduled task when I want to transmit the necessary information.

All was well and good until I checked my log files and found quite a few SMTP send errors. How strange. I use log4net for logging in most of my work so I tailed the log file using the excellent LogView Plus utility. From that I was able to determine that the send utility only failed when my workstation was connected to my client's VPN. And so the plot thickened.

Like most of you readers my ISP, a major carrier in the northeastern United States, blocks traffic on port 25 as an anti-spam method. The only SMTP server that can be reached is the ISP's own server and only if you're connected to their network. However, when connected to my client's VPN the SMTP traffic was being routed over their network and my ISP's mail server would reject the connection since it looks like an unauthorized relay attempt.

My domain has a mail server that I can use but I can't reach it via the normal port 25 access because of the aforementioned ISP blocking. However the server can be reached via port 465 if transmitting over SSL. This port is no longer specifically for SMTPS but it works with my mail server so if I adjust a few settings in my code I should be fine. How typically naive of me.

Explicit Implications

So I modified my code that connects to the SMTP server to send to look like this:

When execution got to the Send method there was a long wait, then an exception. I wasn't connected to the VPN but clearly something had timed out. The error indicated a failure sending mail (duh!) with the additional inner exception "Unable to read data from the transport connection: net_io_connectionclosed" (huh?!?).

After much head scratching I was able to determine that the mail server, when monitoring port 465, is expecting all traffic over that port to be encrypted using SSL. That means the certificate negotiation happens even before the first SMTP HELO command. Further research indicated that the SmtpClient class does not support this type of transport level security, called Implicit SSL. So what's the point of the SmtpClient EnableSsl property? It turns out that uses a different certificate negotiation procedure. The client connects via an insecure port, specifically the dreaded port 25, and issues a STARTTLS command. The client and server will then negotiate the secure transmission after the explicit request, hence the name Explicit SSL. But this didn't help my situation because port 25 is being blocked by my ISP. Grrrrrr! Even if I tried to use Explicit SSL it wouldn't work because I can't reach my mail server over that port.

Tunnel My Way To Freedom

So I needed some way to send via SMTP over a secure connection that was negotiated before the initial SMTP handshake. I checked various Open Source libraries but they all support only Explicit SSL. I really didn't want to invest a lot of time rolling my own SMTPS implementation (although it would be an interesting project), especially since this was supposed to be a quickie utility.

What I finally located was a utility called Stunnel. It's essentially a secure transport connection between two endpoints. You can use it on a client to redirect traffic to a port to another server/port combination.

DISCLAIMER: Stunnel uses portions of the OpenSSL library, which recently had a high-profile exploit published in all major tech news media. I believe the latest version uses the patched OpenSSL but please use at your own risk.

Once the utility is installed, you use the "stunnel Service Install" entry on the Start Menu to set it up as a service. Before starting the service you need to make a modification to the "stunnel.conf" configuration file. The entry for my particular situation looked like this:

; ************** Example SSL client mode services

[my-smtps]
client = yes
accept = 127.0.0.1:465
connect = mymailserver.com:465

This tells stunnel to accept traffic locally on port 465 and reroute it over a secure channel to my public mail server, A slight change to my code:


...was all I needed to make this work.

The upside of all this was I learned a good deal about SMTP. The downside is that this "quickie" utility took a lot longer than I expected, proving once again that there is no such thing as a "quickie" utility.