For some reason it seems I’m developing an Exchange theme in recent posts, but I came across an issue with this recently so decided to write it up for my own future reference.
You had me at EHLO
This was the name of the Exchange Team blog back in the day and it remains the high watermark of wit for an email nerd. Back when I was an Exchange server admin I could compose a message via telnet almost as quickly as I could do it in a mail client; I knew all of the commands and their sequence like the back of my hand as it was always easier to get the server’s response directly rather than trawl through logs when figuring out a problem.
While this still works for inbound mail, modern secure protocols and authentication mechanisms mean that testing outbound mail from a mailbox is trickier than it used to be. However, even with this added complexity the technique still comes in handy when the only thing for it is to see first-hand what happens when a message is submitted or to be able to tweak specific aspects of a message without a mail client getting in the way.
With Exchange Online now requiring TLS for client submission, telnet won’t cut it. OpenSSL’s s_client is ideal though, and easily installed on Windows via winget:
winget install openssl
Before connecting, we need to ensure we can use SMTP AUTH on the test mailbox. Microsoft recommends this is disabled on the organisation with a per-mailbox override, which can be set with the following cmdlet in Exchange powershell:
Set-CASMailbox -Identity [email protected] -SmtpClientAuthenticationDisabled $true
It’s also best practice to have a conditional access policy blocking legacy authentication; if present, this will need an exception added to permit the connection.
Remember to allow some time (Microsoft says up to 24 hours) for these settings to take effect and don’t forget to reverse them later if they’re just being added for troubleshooting purposes.
Finally, we need the base64-encoded string for username and password. Powershell can provide this:
[Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("[email protected]")) [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("password"))
To connect, run the s_client and don’t forget the -ign_eof switch at the end (see footnote for explanation):
openssl s_client -connect smtp.office365.com:587 -starttls smtp -ign_eof
This will connect, show and verify the TLS certificate from Microsoft and finish up with staus:
Supply the extended hello (EHLO):
>EHLO 250-TY2PR06CA0028.outlook.office365.com Hello [184.108.40.206]
Next, kick off authentication with AUTH LOGIN, then paste first the base64 string for username, wait for the 334 response and then the base64 string for password.
>AUTH LOGIN 334 VXNlcm5hbWU6 >bmljZUB0cnkuY29tLi4uLi4uLi4uLi4= 334 UGFzc3dvcmQ6 >QWRvbnRwdXRwYXNzd29yZHNvbnRoZWludGVybmV0 235 2.7.0 Authentication successful
Once authenticated, we’re ready to submit our test message using MAIL FROM:, RCPT TO: and DATA, finishing with enter/full-stop/enter to submit.
>MAIL FROM:[email protected] 250 2.1.0 Sender OK >RCPT TO:[email protected] 250 2.1.5 Recipient OK >DATA 354 Start mail input; end with <CRLF>.<CRLF> >To: Recipient<[email protected]> >From: TestAccount<[email protected]> >Subject: Testmessage >This is a test message. >. 250 2.0.0 OK
The server will include its message receipt and delivery will commence, or with any luck the server will throw a very specific error that will explain exactly what went wrong. To close the session, just type QUIT or close the window.
The -ign_eof switch
On a recent test, I couldn’t figure out why the connection kept closing after authentication; it would just say DONE and quit. The process would look like this:
>AUTH LOGIN 334 VXNlcm5hbWU6 >bmljZUB0cnkuY29tLi4uLi4uLi4uLi4= 334 UGFzc3dvcmQ6 >QWRvbnRwdXRwYXNzd29yZHNvbnRoZWludGVybmV0 DONE
I finally discovered that where s_client is given a Q at the start of any string, it will interpret it as a quit command. To prevent this from happening, use the -ign_eof switch to “inhibit shutting down the connection when end of file is reached in the input”. This will avoid tearing out of hair in the rare circumstance that the base64 string for username or password begins with a Q.