So, I decided to add a little security to the mail system with SASL auth and TLS. We’ll discuss TLS configuration first because I set Postfix up to only allow TLS logins, so testing whether or not SASL is working later requires that TLS be set up, in this particular case.

First, create a cert that we can use for Postfix, and drop the cert in /etc/pki/tls/certs and the key in /etc/pki/tls/private. Call them what you like, and Google for how to create a self-signed cert with OpenSSL. It’s easy.

TLS support is already built into the CentOS Postfix package. I looked at the documentation on Postfix’s site, and followed the directions, mostly. I added these configuration options to /etc/postfix/main.cf:

smtpd_tls_cert_file = /etc/pki/tls/certs/postfix.pem
smtpd_tls_key_file = /etc/pki/tls/private/postfix.pem
smtpd_tls_mandatory_ciphers = high
smtp_tls_mandatory_protocols = SSLv3, TLSv1
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_security_level = may
smtpd_tls_auth_only = yes
tls_random_source = dev:/dev/urandom

Many of these values’s meanings are obvious, such as the cert and key filenames, and the mandatory ciphers and protocols (which are great for PCI compliance, btw).

Really, the most important option here is smtpd_tls_auth_only, which disallows SMTP authentication if not connected with TLS. We’ll look at this more after we have SASL configured.

I also added the smtpd_tls_security_level option, even though may is the default value because I wanted to bring up an interesting point, also discussed in Postfix’s TLS docs. This value can be set to “encrypt”, which will enforce the use of TLS. This sounds great, but it also has an unwanted side effect: it causes Postfix to accept no mail without TLS encryption! According to RFC 2487, this should not be done on a public-facing SMTP server.

The smtpd_tls_received_header variable sets the Received: header to include regarding which protocols and ciphers were used, as well as the client and issuer Common Name (or domain name). It looks like this:

Received: from 1.2.3.4
(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
(No client certificate requested)
(Authenticated sender: me)
by mail.ozymo.com (Postfix) with ESMTP id 8060188280

Once the configuration is in /etc/postfix/main.cf, restart Postfix. On CentOS:

# service postfix restart

With this, simply configure your mail client to use TLS security for the connection and you are set up. Let’s move on to SASL auth.

I installed the Cyrus-SASL packages on my CentOS box with the following command:

# yum install cyrus-sasl

Once that was done, I needed to configure Postfix to correctly use the SASL libraries.  I added these configuration options to /etc/postfix/main.cf:

## SASL Configuration
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_path = smtpd
smtpd_sasl_security_options = noanonymous
smtp_sasl_security_options = noplaintext
broken_sasl_auth_clients = yes

Once this configuration is in place, we have to make sure that the right settings are in the SASL smtpd.conf file. The locate command will be helpful in locating this file. On my box, 64-bit CentOS, it’s here:

$ locate smtpd.conf
/usr/lib64/sasl/smtpd.conf
/usr/lib64/sasl2/smtpd.conf

The one we want is in sasl2.

$ cat /usr/lib64/sasl2/smtpd.conf
pwcheck_method: saslauthd

These settings authenticate against the regular UNIX password database, ./etc/passwd and /etc/shadow. There are plenty of other ways to do this, but they are beyond the scope of this doc. Below, when we connect via telnet, you’ll see the default list of authentication mechanisms that SASL uses. To adjust this, add in the mech_list: section, as follows:

mech_list: PLAIN LOGIN

PLAIN and LOGIN are very common, and are the most often used.

After I had all my configurations in place, I restarted postfix, and logged into the server via telnet (snippet here):

250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

So where is SASL? TLS is there, but no SASL. I’ve configured it in main.cf, and restarted Postfix. So what gives? Here’s the kicker:

smtp_sasl_security_options = noplaintext

So, if I login via regular port 25, I do not get the option to authenticate with SASL. When this is combined with:

smtpd_tls_auth_only = yes

then SASL authentication can only occur when connecting with TLS security, giving two layers. Let’s connect with openssl and check things out:

$ openssl s_client -connect oz:25 -starttls smtp
CONNECTED(00000003)
. . . {cert info}
SSL handshake has read 1770 bytes and written 351 bytes

New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
. . . {TLS session info}
250 DSN
EHLO mail.ozymo.com
250-mail.ozymo.com
250-AUTH CRAM-MD5 DIGEST-MD5 PLAIN LOGIN
250-AUTH=CRAM-MD5 DIGEST-MD5 PLAIN LOGIN
250-8BITMIME
250 DSN

As you can see, the SASL lines are now present:

250-AUTH CRAM-MD5 DIGEST-MD5 PLAIN LOGIN
250-AUTH=CRAM-MD5 DIGEST-MD5 PLAIN LOGIN

According to Postfix’s SASL doc, referenced above, older Microsoft SMTP client software implements a non-standard version of the AUTH protocol syntax, and expects that the SMTP server replies to EHLO with “250 AUTH=mechanism-list” instead of “250 AUTH mechanism-list”. If you look at the config above, you can see that this is the reason both lines show up. Otherwise, we’d just see the first. Ah, to pine over a perfect Microsoft-free world! We’re down one app for now, though.  :)

Via telnet, you can login by issuing AUTH LOGIN at the prompt followed by a hash of your username and password:

AUTH PLAIN AHRlc3QAdGVzdHBhc3M=
235 2.0.0 Authentication successful

The above is a hash of user “test” and password “testpass” so it won’t work; sorry. To generate a hash is as simple as a line of perl, but I wrapped it in a script I call smtpencode64. Here’s the source:

#!/bin/bash
if [ -z "$2" ]
then
echo “Usage: smtpencode64 <username> <password>”
else
perl -MMIME::Base64 -e “print encode_base64(\”\000$1\000$2\”)”
fi

Put it in your ~/bin and give it execute permissions, and you should be al set. Run it like so:

$ smtpencode64 test testpass
AHRlc3QAdGVzdHBhc3M=

Don’t forget to update your mail client, or you will be denied access to that ever-important new message.

/cs