Setting up SMTP authorization for Postfix using Cyrus SASL
  1. 1. Other posts about Postfix and Cyrus
  2. 2. Intro – why use SMTP auth?
  3. 3. Setting up Postfix to use SMTP authorization
    1. 3.1. Postfix & SASL
    2. 3.2. Back to configuration
  4. 4. Testing SMTP authorization
  5. 5. Last touches
  6. 6. Common glitches
    1. 6.1. Verbose logging of SMTPd
    2. 6.2. SASL: «No user in database»
    3. 6.3. Still nothing
Other posts about Postfix and Cyrus
  1. Your Own Mail Host – Postfix, Cyrus, FreeBSD
  2. Setting up SMTP authorization for Postfix using Cyrus SASL
  3. Setting up secure Cyrus
  4. Multidomain mail system with Postfix & Cyrus
  5. Cyrus & SASL«No Mechanism Available»

While stalking the Internet I have stumbled upon this handy script that collects info about your SASL installation and SMTP auth configuration and display it – I almost haven't used it but it looked pretty nice so I thought I'd share it with you.

It needs bash to run (on my FreeBSD 8 it wasn't installed by default, you can get it from /usr/ports/shells/bash/.

Intro – why use SMTP auth?

First of all let's remember a strange-sounding config option of Postfix' main.cf file which we saw yesterday when setting it up: it's mynetworks_style. Until today I didn't realy realize what it was for and why have «trusted» and «strangers» when receiving incoming mail. I didn't care and set it to subnet (as it was by default) and everything worked fine.

However, today's morning I got lazy and attempted to send an e-mail using my cell phone – and got a familiar Relay access denied message. Why?

I powered up my brain (not the easiest thing in the morning :P) and remembered that I read something like this in the Postfix' main config file:

The mynetworks parameter specifies the list of «trusted» SMTP clients that have more privileges than «strangers».

In particular, «trusted» SMTP clients are allowed to relay mail through Postfix. See the smtpd_recipient_restrictions parameter in postconf(5).

Than a thought struck me: why, there's actually a difference for Postfix if I'm sending mails from my home PC (that's inside my server's network) or from anything else? How I'm supposed to give accounts to other users who are outside my network? List every existing mail host in the work in relay_domains or all my users in mynetworks? Then what about their dynamic IPs?
I actually had a hunch like that yesterday but I didn't really believe that it makes senseto restrict sending mail from within host's subnet only.

Then I thought of an easy hack like mynetworks = 0.0.0.0/32 but at this point my mind finally sobered up and I exclaimed: «Aha!».
Well, the idea seems to be really easy: we can't let anyone use our host to leavecorrespondence to be sent somewhere else – from our machine, usnig our CPU, domain name, IP and everything else. Spammers would be happy but we probably won't.

«But what's the deal? We can access IMAP, we're authorized already, what's the problem?» – I thought so. However, another brilliant thought struck me and I thought back:

Wait, are we really authorized?

And that was where everything has started.

Setting up Postfix to use SMTP authorization

In fact, if we think about this carefully then no, we're not using any auth mechanism for SMTP – even if we have Cyrus serving us IMAP our SMTP (handled by Postfix) is gaped widely to every one willing to use it.
With one small exception: Postfix smartly allows him to send e-mail to a very limitednumber of destinations – by default only the server itself (see relay_domains).

In fact, SMTP is so independent of IMAP or Cyrus that in theory (if we manage to avoid SASL) we can have Postfix with its own SMTP auth with no IMAP4/POP3/whatever.

Configuration process starts from the Postfix' master file – /usr/local/etc/postfix/master.cf. Here Postfix services are defined (similar to system's /etc/services). We need to enable SMTPd and at least one SMTP (I don't know why this is so but without at least one of smtp SMTPd won't start up). I've enabled both smtp's just in case.

confsmtp      inet  n       -       n       -       -       smtpd -v
smtp      inet  n       -       n       -       1       postscreen
smtpd     pass  -       -       n       -       -       smtpd -v

The -v flag makes SMTPd run in verbose mode which certainly helps us finding our way around some glitches.

Let's proceed to Postfix' main config file – /usr/local/etc/postfix/main.cf. Append the following lines there:

confsmtpd_sasl_auth_enable = yes
# 3 lines below are security/compatibility enhancements.
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_sasl_authenticated_header = yes

# the rest of lines control how we trust sender and recepient when they talk to us on SMTP.
smtpd_recipient_restrictions =
  reject_unauth_pipelining,
  permit_mynetworks,
  permit_sasl_authenticated,
  check_relay_domains,
  reject_non_fqdn_recipient,
  reject_unauth_destination,
  check_policy_service inet:127.0.0.1:60000,
  permit

smtpd_sender_restrictions =
  permit_sasl_authenticated,
  permit_mynetworks,
  reject_non_fqdn_sender,
  reject_unknown_sender_domain,
  reject_unauth_pipelining,
  permit

After this we need to add our realm for SASL but before that let's stop for a little lesson on how SASL works with Postfix.

Postfix & SASL

In a nutshell, SASL tells the calling program if a password matches user name in some specific database and using some specific auth machanism (called «mech»). One system can have a lot of SASL clients – even our mail host has IMAP and SMTP servers which can use different SASL user registries and settings.
Applications that use SASL in addition to user name can also pass arealm – just like in e-mail address where part after «@» is called domain in SASL's terminology it's called realm.

In addition to that each applciation (called service) can have its own config file. These config files were a major mess when I was going through all the trials of setting up SMTP auth.
Nobody seemed to know when they're located on a particular system (Linux, CentOS, FreeBSD,etc.). After some searching I found that what we need is smtpd.conf and it is supposed to be created by Postfix or Cyrus installer in ports tree. But where?

The answer is: /usr/local/lib/sasl2/smtpd.conf. It looks trivial to me now but you can bet it wasn't just a few hours ago!

By the way, you might have faced a 2 strange groups of Postfix options: smtp_ and smtpd_e.g. smtpd_sasl_auth_enable. The first (without d) sets options for Postfix SMTP client – if you want you can use them to make Postfix log into remote SMTP servers when it sends mail. The second group («smtpd) is about SMTP server – exactly what we need here.

A quick note on other interesting Postfix-SMTP-SASL options:

smtpd_sasl_local_domain
Corresponds to SASL realm (explained below).
smtpd_sasl_path
Name of conf file that SASL uses when it talks to Postfix. I think this is the same as service. Default value of «smtpd» refers to /usr/local/lib/sasl2/smtpd.conf.

You can get a complete reference of Postfix options by calling shman 5 postconf (search in manual page browser by pressing "/" and repeat by «n»).

Back to configuration

So with this new knowledge we create /usr/local/lib/sasl2/smtpd.conf (it probably won't exist yet) and enter the following lines:

confpwcheck_method: auxprop
auxprop_plugin: sasldb

Make sure its permissions are correct:

-r--r--r--  1 postfix  mail  137 Jan 20 12:39 /usr/local/lib/sasl2/smtpd.conf

What we've done is instructed SASL use /usr/local/etc/sasldb2.db – common registry that is also used by Cyrus' SASL. Other option would be using saslauthd which you can install from /usr/ports/security/cyrus-sasl2-saslauthd/. Running a daemon instead of giving direct access to sasldb2.db is somewhat better as a daemon runs as root while its clients have minimal rights, without access to the password DB. In our, though, case we're only using SASL for Cyrus and Postfix so it's simple, easy and relatively secure (they share the same group anyway).

Check the perms of SASL's database:

-rw-r--r--  1 cyrus  mail  16384 Jan 20 12:35 /usr/local/etc/sasldb2.db

Now we need to add a crusial config option to main.cf that tells Postfix which realm to use when talking to SASL. Determine it first:

shell$ sasldblistusers2
cyrroot@example.com: userPassword
proger@example.com: userPassword

In our case the realm is my.i-forge.net. Now add it to main.cf:

smtpd_sasl_local_domain = my.i-forge.net

Okay, things seem up to normal now so we proceed to testing – but first tell Postfix that its config has changed by doing shpostfix reload.

Testing SMTP authorization

shsmtptest is an extremely handy tool when it comes to testing SMTP server. Remember shimtest in previous part of this guide? This is very similar to it.

shell$ smtptest -a cyrroot localhost
C: EHLO example.com
S: 250-i-forge.net
S: 250-PIPELINING
S: 250-SIZE 10240000
S: 250-VRFY
S: 250-ETRN
S: 250-AUTH NTLM LOGIN PLAIN GSSAPI OTP DIGEST-MD5 CRAM-MD5
S: 250-AUTH=NTLM LOGIN PLAIN GSSAPI OTP DIGEST-MD5 CRAM-MD5
S: 250-ENHANCEDSTATUSCODES
S: 250-8BITMIME
S: 250 DSN
Segmentation fault (core dumped)

Come on, another segfault? «Just what's wrong with this box…» – but no panic! We have already had this issue with shimtest – it happens on some systems with some auth methods – on my FreeBSD 8 it happens when smtp/imtest attempt using NTLM (it doesn't happen if another client uses NTLM when talking to my Postfix or Cyrus, though).
So we just need to switch to another auth mech.

But first let us examine Postfix-SMTP reply.

  1. 250-AUTH NTLM LOGIN PLAIN GSSAPI OTP DIGEST-MD5 CRAM-MD5 – this line means that we've made Postfix request our authentication.
  2. 250-AUTH=NTLM ... – note the equality sign after AUTH – some e-mail clients don't recognize the previous line as a proper login request so this line is also send along. It was turned on by broken_sasl_auth_clients = yes in out main.cf.

We can pick any auth mech we see in any of the AUTH lines and call shsmtptest again:

shell$ smtptest -m cram-md5 -a cyrroot localhost
...
Please enter your password:

That's the talk – we've got a password prompt. Proceed…

C: cHJvZ2VyIGE3NGUyNzczZGQ3YjY0YjllOWU0YTM5NmUzYjg1Y2M5
S: 235 2.7.0 Authentication successful
Authenticated.
Security strength factor: 0

Yahoo! Postfix let us in. Great! Exit by pressing Ctrl+D.

«Security strength factor» determines how strong the client-server connection is – the vale of 0 means it's unencrypted, 1 means integrity protection is engaged and the above stends for the encryption strength (in bits) – for example, 256 means we are behind AES-256 or similar provided via a SSL/TLS layer.

If you're getting a message like the following from SMTP:

S: 535 5.7.8 Error: authentication failed: authentication failure
Authentication failed. generic failure

…then you did something wrong with SASL – see the Common glitches section.

That's all we had to do – get your favourite e-mail client and make it use some password sending mechanism it has – e.g. NTLM – and spam around as much as you want from any place on the Earth you're currently in :)

Last touches

These steps are not required but will make our instalation more neat. Remember to refresh running Postfix with shpostfix reload.

As you remember, Postfix has a concept of «trusted» clients which are allowed sending mails everywhere through it. However, since we're using SMTP authrization now so we can change «trusted» clients from all the server subnetwork to just the server machine itself.
Do so by setting: mynetworks_style = host in /usr/local/etc/postfix/main.cf.

Also, many tutorials that I had ran over during my SMTP trial suggested restricting **SASL mechs** to just plain & login for SMTP. I don't know the reason for this (it works fine for me and Thunderbird with full set of mechs and any one of them is better than transmitting plain text password) but if you need it add this line to /usr/local/lib/sasl2/smtpd.conf:

mech_list: plain login

Common glitches

Verbose logging of SMTPd

The first thing you need to do when running into troubles with any Postfix service – enable their verbose logging. This is done in /usr/local/etc/postfix/master.cf. Find that service's line and add -v flag (or more for more verbosity: -vv, -vvv) to its executable (specified in the last column):

smtpd     pass  -       -       n       -       -       smtpd -v

If the service takes up several lines just add «-v» to the end of the first line. After this call shpostfix reload and produce some activity using shsmtptest while checking /var/log/maillog for new lines – SMTPd produces a lot of debug messages in this mode.

Tip: you can make all log messages be sent to one file (/var/log/all.log) so it's more convenient to see then with one extra terminal by uncommenting (or adding) the following line to /etc/syslog.conf:

conf*.*                                             /var/log/all.log

After this create log file by touching it and setting proper perms:

shell$ touch /var/log/all.log
$ chmod 0600 /var/log/all.log

Don't forget to restart syslogd by doing shkillall -HUP syslogd. You can then watch all appearing log messages using tail (-f makes it wait for new messages and output them as they come in):

shell$ tail -100 -f /var/log/all.log

SASL: «No user in database»

A most troublesome moment were SASL errors like these:

logJan 20 10:38:10 my postfix/smtpd[40468]: warning: localhost[127.0.0.1]: SASL LOGIN authentication failed: authentication failure
Jan 20 12:38:06 my postfix/smtpd[64667]: warning: SASL authentication failure: no user in db
Jan 20 13:50:27 my postfix/smtpd[2366]: warning: SASL authentication failure: no secret in database
Jan 20 13:50:27 my postfix/smtpd[2366]: warning: unknown[192.168.5.4]: SASL NTLM authentication failed: authentication failure

By deducing and brainstorming I came up with the following reasons for this:

  1. Most obvious but least often cause is that there's indeed no used in sasldb2.db – use sasldblistusers2 to see who is registered and shsaslpasswd2 to manage them.
  2. Application that uses SASL passed it wrong realm and user like «myself» ends up like «myself@wrongrealm» instead of «myself@mybox». See smtpd_sasl_local_domain of main.cf and its configuration above.
  3. «No secret in database» might also mean that passwords didn't match – in this case enable verbose mode of smtpd server and see its decoded messages.
  4. Wrong setup of SASL – this is likely the most common problem so we'll discuss it here in detail.

First off, check that your smtpd.conf is actually read by SASL – do this by adding some junk text to /usr/local/lib/sasl2/smtpd.conf and tryign connecting to it via shsmtptest – if nothing happens (no output from server) then it's read.
Less brutal way of checking this is adding there a line like this:/usr/local/lib/sasl2/smtpd.conf:

mech_list: plain login

…and again trying connect to SMTP via smtptest – examine its output and see if line(s) starting with «S: 250-AUTH» don't ahve anything like CRAM-MD5 or NTLM:

S: 250-AUTH LOGIN PLAIN

Don't forget to do postfix reload after changing any of its configuration files.

If smtpd.conf is correctly parsed by SASL check which means it uses to verify passwords. The first thing is pwcheck_method which among other values (some of which are described here) can be of these:

sasldb
Use SASL user registry which is located in /usr/local/etc/sasldb2.db.
saslauthd
Use SASL auth daemon – for this to work you need it installed. As already mentioned above, it's located in /usr/ports/security/cyrus-sasl2-saslauthd/ but for a simple mail host it can be avoided.
auxprop
Use some other password provider.

sasldb will not work for Postfix SMTP authentication directlyi.e. don't specify it as pwcheck_methodSMTP server will fail with errors similar to the ones in the beginning of this section. AFAIK this happens because Postfix expects passwords to be stored in plain text form which isn't the case with sasldb.

saslauthd will work and generally needs the same configuration as auxprop.

auxprop will work too but it needs extra config options. Usually only auxprop_plugin is mandatory. It can have of 3 values:

sasldb
Accounts are taken from common SASL database – sasldb2.db.
sql
Uses a SQL database to retrieve accounts from.
ldapdb
Uses LDAP database.

We'll only discuss auxprop_plugin=sasldb here. Its effect is similar to pwcheck_method=sasldb but, unlike it, it will work even with Postfix – even though SASL stores encrypted passwords there. Just set this option, make sure /usr/local/etc/sasldb2.db is accessible by Postfix and everything should work.

-rw-r--r--  1 cyrus  mail  16384 Jan 20 12:35 /usr/local/etc/sasldb2.db

See also SASL_README page.

Still nothing

If you've followed all instructions above, read zillion of Google pages, scratched your head and done other things – and SMTP still doesn't work – then you might be facing the same situation that I did otday.
It's easy to resolve. Just give up.

No, seriously, I have almost given up on this thing and went shopping for foods. After returning I have decided to give shsmtptest just one more try – and it worked! Out of the blue, I didn't change anything and wasn't doing something different than an hour ago. It just worked like a charm :)
(No, I wasn't forgetting to reload Postfix.)

So who knows – maybe it's your case as well. Just go do something for an hour and then come back to see if proto-AI self-restorable consciousness of FreeBSD has done the work for you.

Because it might.