User Tools

Site Tools


exim_smtp_authentication_against_ldap

Exim SMTP Authentication against LDAP

I wanted to block repeated failed attempts against SMTP auth using fail2ban, and the easiest way for to achieve that was to have fail2ban block based on the exim4 mainlog.

2017-09-09 13:19:03 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected])
2017-09-09 13:24:19 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected])
2017-09-09 13:29:34 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected])
2017-09-09 13:34:49 login authenticator failed for (User) [80.82.77.175]: 535 Incorrect authentication data ([email protected])

All the mail users are stored in LDAP, anonymous bind searching is enabled, and I wanted to re-bind to check the authentication. I had to get the DN from the UID supplied by the SMTP connection. This is a pretty common method (used by dovecot, etc) but it's not directly supported by Exim without some extra lookups.

Fortunately exim has all the bits and pieces needed to do the LDAP lookups, it's just a matter of stringing them together.

I found 'exim -be' to be very handy in debugging the exim expansions for each section.

First we need to find the user's LDAP DN based on a uid lookup of the supplied username:

${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}}

Connect to the LDAP server on localhost, use a BaseDN of ou=mail,dc=domain,dc=ca and return all attributes using ??, and a subtree search, with a filter (uid=…)

:~$ exim -be
> ${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}}
cn=Test User,ou=domain.ca,ou=mail,dc=domain,dc=ca
> ^D

And a little sub-shell substitution to check the result with the 'ldapsearch' command

:~$ ldapsearch -xvvv -D$(exim -be '${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:[email protected]})}}') -W uid=test\* dn

Based heavily on the example from the Exim documentation, the result is:

plain:
  driver = plaintext
  public_name = PLAIN
  server_condition = ${if and{{ !eq{}{$auth2} }{ \
    ldapauth{\
      user="${quote_ldap:${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:$auth2})}}}" \
      pass=${quote:$auth3} \
      ldap://localhost/} }} }
  server_set_id = $auth2
  server_prompts = :
  
login:
  driver = plaintext
  public_name = LOGIN
  server_prompts = “Username:: : Password::”
  server_condition = ${if and{{ !eq{}{$auth1} }{ \
    ldapauth{\
      user="${quote_ldap:${lookup ldapdn{ldap://localhost/ou=mail,dc=domain,dc=ca??sub?(uid=${quote_ldap:$auth1})}}}" \
      pass=${quote:$auth2} \
      ldap://localhost/} }} }
  server_set_id = $auth1

This can be tested first by using exim debug:

Generate the base64 encoded nullusernamenullpassword string needed for auth plain
$ echo -en '\[email protected]\0testpass' | base64
AHRlc3R1c2VyQGRvbWFpbi5jYQB0ZXN0cGFzcw==

auth login uses 2 separate base64 strings for username (4oCcVXNlcm5hbWU6) and password (UGFzc3dvcmQ64oCd)

$ echo -n '[email protected]' | base64
dGVzdHVzZXJAZG9tYWluLmNh
$ echo -n 'testpass' | base64
dGVzdHBhc3M=
:~$ exim4 -bhc 66.55.44.33 -d+all
...
220 mail.domain.ca ESMTP Sat, 09 Sep 2017 17:14:32 -0400
17:14:32 22572 smtp_setup_msg entered
ehlo mailhost.com
...
250-mail.domain.ca Hello mailhost.com [66.55.44.33]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN LOGIN
250-STARTTLS
250 HELP
17:14:40 22572 SMTP>> 250-mail.domain.ca Hello mailhost.com [66.55.44.33]
17:14:40 22572 250-SIZE 52428800
17:14:40 22572 250-8BITMIME
17:14:40 22572 250-PIPELINING
17:14:40 22572 250-AUTH PLAIN LOGIN
17:14:40 22572 250-STARTTLS
17:14:40 22572 250 HELP
auth plain
17:14:53 22572 SMTP<< auth plain
17:14:53 22572 SMTP>> 334 
334
...
AHRlc3R1c2VyQGRvbWFpbi5jYQB0ZXN0cGFzcw==
...
17:19:47 22572 expanding: $auth2
17:19:47 22572    result: [email protected]
17:19:47 22572 SMTP>> 235 Authentication succeeded
235 Authentication succeeded
quit

And then remotely using gnutls-bin for starttls

:~$ gnutls-cli --starttls --port 25 mail.domain.ca
Processed 174 CA certificate(s).
Resolving 'mail.domain.ca'...
Connecting to '77.66.55.44:25'...

- Simple Client Mode:

220 mail.domain.ca ESMTP Sat, 09 Sep 2017 16:34:53 -0400
ehlo mailhost.com
250-mail.domain.ca Hello dhcp.provider.com [44.33.22.11]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN LOGIN
250-STARTTLS
250 HELP
starttls
220 TLS go ahead
^d
*** Starting TLS handshake 
...
ehlo mailhost.com
250-mail.domain.ca Hello dhcp.provider.com [44.33.22.11]
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-AUTH PLAIN LOGIN
250-STARTTLS
250 HELP
auth login
334 4oCcVXNlcm5hbWU6
dGVzdHVzZXJAZG9tYWluLmNh
334 UGFzc3dvcmQ64oCd
dGVzdHBhc3M=
235 Authentication succeeded
quit

This fail2ban already had a /etc/fail2ban/filter.d/exim.conf file, so I could just use it as filter in a new section of /etc/fail2ban/jail.local

[exim]
enabled = true
port = all
filter = exim
logpath = /var/log/exim4/mainlog

You will probably want to have Exim listen to alternate ports (465/587), and allow auth only on encrypted connections:

daemon_smtp_ports = 25 : 465 : 587
tls_on_connect_ports = 465
auth_advertise_hosts = localhost : ${if eq{$tls_cipher}{}{}{*}}

Ref: http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_plaintext_authenticator.html

exim_smtp_authentication_against_ldap.txt · Last modified: 2020/02/13 22:55 (external edit)

free spam filter