The Exim FAQ

Contents   Previous   Next

7. POLICY CONTROLS

Q0701:  How do I block unwanted messages from outside my host?

A0701:  Exim uses Access Control Lists (ACLs) for controlling incoming mail from other hosts. A whole chapter in the reference manual is devoted to describing how they work. A wide variety of conditions can be imposed on incoming messages.

The default Exim run time configuration contains an example of an ACL which blocks all relaying, and messages whose senders cannot be verified. This example is heavily commented and worth studying.

Q0702:  I don't want to block spam entirely; how can I inspect each message before deciding whether or not to deliver it?

A0702:  Wherever possible, inspection and rejection is best done automatically in an ACL, that is, before the message is accepted. If you want to verify manually each message that is classified as spam by an automatic check, you can arrange for a system filter to freeze such messages after they have been accepted.

If, after inspection, you decide not to deliver the message, it is safest to discard it, using the -Mrm option. Use of the -Mg option to force a bounce carries the risk of “collateral spam” if the sender address is faked.

Q0703:  How can I test that my spam blocks are working?

A0703:  The -bh option allows you to run a testing SMTP session as if from a given IP address. For example,

   exim -bh 192.168.178.39

In addition to the normal SMTP replies, it outputs commentary about which tests have succeeded or failed. If you are not interested in the details, but just want to know if a particular sender at a particular IP address is able to mail to a particular recipient, you can use the exim_checkaccess utility, which provides a “packaged” version of -bh. You call it like this:

   exim_checkaccess 192.168.53.23 recip@my.domain -f sender@some.domain

If you don't give a sender, <> is used (that it, it acts like a bounce message).

Q0704:  How can I test that Exim is correctly configured to use the Realtime Blackhole List (RBL)?

A0704:  The -bh option allows you to run a testing SMTP session as if from a given address. The exim_checkaccess utility provides a more packaged version of this facility. You need to know a blocked IP address with which to test. Such a testing address is kindly provided by Russell Nelson:

   linux.crynwr.com [192.203.178.39]

You can also send mail to nelson@linux.crynwr.com from the server whose RBL block you are testing. The robot that receives that email will attempt to send a piece of test email in reply. If your RBL block didn't work, you get a message to that effect. Regardless of whether the RBL block succeeds or not, it emails you the results of the SMTP conversation from a host that is not on the RBL, so you can see how your server looks from the view of someone on the RBL.

Q0705:  How can I use tcpwrappers in conjunction with Exim?

A0705:  Exim's own control facilities can do all that tcpwrappers can do. However, if you are already using tcpwrappers for other things it might be convenient to include Exim controls in the same place.

First of all, ensure that Exim is built to call the tcpwrappers library, by including USE_TCPWRAPPERS=yes in Local/Makefile. You also need to ensure that the header file tcpd.h is available at compile time, and the libwrap.a library is available at link time, typically by including it in EXTRALIBS. You may need to copy these two files from the tcpwrappers build directory to, for example, /usr/local/include and /usr/local/lib, respectively. Then you could reference them by

   CFLAGS=-I/usr/local/include
   EXTRALIBS=-L/usr/local/lib -lwrap

in Local/Makefile. There are two ways to make use of the functionality, depending on how you have tcpwrappers set up. If you have it set up to use only one file, you ought to have something like:

   /etc/hosts.allow:
   exim : <client_list>  : <allow_or_deny>

For example:

   exim : LOCAL  192.168.0.  .friendly.domain  special.host : ALLOW
   exim : ALL                                               : DENY

This allows connections from local hosts (chiefly localhost), from the subnet 192.168.0.0/24, from all hosts in *.friendly.domain, and from a specific host called special.host. All other connections are denied. If you have tcpwrappers set up to use two files, use the following:

   /etc/hosts.allow:
   exim    : <client_list>
   /etc/hosts.deny:
   exim    : <client_list>

Read the hosts_access man page for more ways of specifying clients, including ports, etc., and on logging connections.

Q0706:  How can I get POP-auth-before-relay (aka POP-before-SMTP) support in Exim?

A0706:  Exim 4 supports the “whoson” (http://whoson.sourceforge.net) facility for doing this. If you set this up, you can do the check in an Exim ACL by a statement like this:

   require condition = \
     ${lookup whoson {$sender_host_address}{yes}{no}}

Otherwise you need to arrange for a list of permitted IP addresses to be maintained in a file or database, and use this in a hosts condition in an ACL statement. An Exim user has published this recipe:

    http://www.zeiss.cx/memo/computer/linux/email/exim-s-a-p.html

Another Exim user submitted the following idea:

Use a script to grab authenticated IP addresses from the log files of the POP3 and IMAP4 daemons. These are used to create files in the directory tree /var/db/popb4smtp. The existence of a file represents a valid “popped recently token” for the IP address used as the filename.

Another script periodically removes stale files from the tree (after two hours). There's a small race condition here; it's possible for a file to be deleted just after it has been updated by the script that watches the logs. For low-volume servers, the odds of hitting this window are low.

A POPB4SMTP_CLIENT macro in the Exim configure file provides a reusable “has this sender popped recently?” query:

   POPB4SMTP_SUBDIR = /var/db/popb4smtp/${substr_-1_1:$sender_host_address}
   POPB4SMTP_CLIENT = ${if exists {POPB4SMTP_SUBDIR/$sender_host_address} \
       {$sender_host_address} {0} }

Now you can use it just about anywhere, including in your ACLs. Simple examples include:

   hostlist relay_hosts = 127.0.0.1/32 : ... : POPB4SMTP_CLIENT
   host_lookup = !127.0.0.1/32 : ... : !POPB4SMTP_CLIENT
   rfc1413_hosts = !127.0.0.1/32 : ... : !POPB4SMTP_CLIENT

The two scripts (and a FreeBSD startup script for them) are available for download at:

    http://people.FreeBSD.org/~sheldonh/popb4smtp-nodb.tar.gz

Q0707:  I have one or two cases where my host correctly rejects messages, but the remote host is quite persistent, and keeps trying over and over.

A0707:  It is an unfortunate fact that a number of SMTP clients, in violation of the SMTP RFC, do not treat a permanent error code that is given after the DATA portion of the transaction as a permanent error. Consequently they keep resending the message, and the worst offenders do so at very short intervals.

The only way to stop such behaviour is to blacklist the IP address, or the envelope sender, or both, in such a way that future messages get rejected at RCPT time instead of at DATA time. You could also complain to the remote host's administrators.

Q0708:  How can I run customized verification checks on incoming addresses?

A0708:  There are a number of possibilities:

(1)  If you can implement your checks in Perl, you can use Exim's facility for running an embedded Perl interpreter. For example, if you want to run special checks on local addresses, you could use ACL an statement like this:

   require domains = my.local.domain
           condition = ${perl{verify}{$local_part}}

The result of the Perl function should be “yes” or “no”.

(2)  You could also run an external program in a similar way, by a statement such as:

   require domains = my.local.domain
           condition = ${run{/my/verifier $local_part}}

This requires the use of another process, so could prove more expensive than Perl.

(3)  If you are prepared to write C code, read the chapter in the manual entitled Adding a local scan function to Exim.

Q0709:  Does Exim apply RBL checks to error messages, those with an envelope sender of <> ?

A0709:  This depends on the ACL configuration. You can test for bounce messages (by looking for an empty sender address) and thereby exclude them from RBL checking if you want. This ACL statement does that:

   deny senders = ! :
        dnslist = blackholes.mail-abuse.org

However, some spam does come with an empty sender address, so this may not be a good idea.

Q0710:  I want to reject certain sender-recipient combinations, with a specific message for each such combination.

A0710:  Set up a file (or database) containing the messages, keyed by the combination, for example:

   sender1@sdomain1=>recipient1@rdomain1: blocked because...
   sender2@sdomain2=>recipient2@rdomain2: blocked because...

If you have lots of recipients for the same sender, it might be easier to generate this file from more convenient data. In your ACL that is run for each RCPT command, you can then put:

   deny message   = ${lookup{$sender_address=>$local_part@$domain}\
                    lsearch{/that/file}}
        condition = ${lookup{$sender_address=>$local_part@$domain}\
                    lsearch{/that/file}}{yes}{no}}

The condition is tested first. If the lookup succeeds, the condition succeeds so access is denied. The message is then expanded, but the lookup won't be repeated, because Exim will have cached the previous result.

This approach blocks only incoming SMTP messages. If you need to do similar blocks for messages that do not arrive over SMTP, you have to set up a suitable redirect router with a :fail: setting.

Q0711:  Will Exim allow me to create a file of regexs and match incoming external email to the list - and if a match is found file the offending message into a special location? Also is it possible to make Exim only filter parts of an incoming email - e.g. ignore large MIME attachments for example and only process text/plain?

A0711:  You can do some of this in a system filter. For example:

   if $message_body matches <...some complicated regex...> or
      $message_body matches <...some other regex...> or
      $header_from: matches <...regex...> or
      etc.
   then
     save /some/special/file
   endif

or instead of save you could have deliver (to some address) or pipe (to some script).

There isn't any mechanism for ignoring attachments, but $message_body only looks at the first n bytes of the body, where n defaults to 500 but can be changed.

A more expensive alternative would be to run a Perl subroutine using the embedded Perl mechanism. If you passed over the message id, the Perl code could read the message files on the spool and implement any algorithm it liked for deciding what should be done.

Q0712:  I've hacked sendmail to make an ioctl call at the time of the SMTP RCPT command, to check if a user has exceeded their email quota. If they have I issue a temporary failure and a message - can I do this with Exim?

A0712:  If you can make this happen in Perl you can use the embedded Perl facility, and use it from a condition condition in an ACL statement. You can also use the expansion facility to run an external program, but this uses more resources because it uses another process.

Q0713:  I'd like to pass all messages through a virus-scanning system before delivery. Can Exim do this?

A0713:  One way of achieving this is to deliver all messages via a pipe to a checking program that resubmits them for delivery in some private way that can be checked (e.g. on a specific SMTP port, or IP address). One possibility is to use the `received protocol` field that can be set for locally submitted mail via the -oMr command line option. This router sends all messages that are not from the local host and whose received protocol is not scanned-ok to the virus_scan transport:

   vircheck:
     driver = accept
     transport = virus_scan
     condition = ${if or {{eq {$received_protocol}{scanned-ok}} \
                          {eq {$sender_host_address}{127.0.0.1}}}\
                          {0}{1}}

One problem is that this approach scans the message for each recipient, not just once per message.

The virus_scan transport should be set up to pipe the message to a suitable checking program or script which runs as a trusted user. This can then re-submit the message to Exim, using -oMr to set the received protocol to scanned-ok, and the -f option to set the correct envelope sender address. Warning: If you forget to make the resubmitting process run as a trusted user, the received protocol does not get set, and you are likely to generate a loop.

Q0714:  Is there a way to configure Exim to reject mail to a certain local host?

A0714:  No, only to certain domains. To reject at SMTP time, you can put a line like this in your ACL:

   deny message = this domain is deliberately rejected
        domains = a.certain.domain

To fail addresses in messages that do not arrive over SMTP, you can set up a router like this:

   reject_a_certain_domain:
     driver = redirect
     domains = a.certain.domain
     allow_fail
     data = :fail: this domain is deliberately rejected

Q0715:  How can I get Exim to remove attachments from messages?

A0715:  Exim does not contain facilities for modifying messages. You must use an external program if you want to do this. You can route messages that have a Content-type: header line via a pipe to a command that does the job and then re-submits the message to Exim. Alternatively, you could use a transport filter to do this job.

Q0716:  How can I arrange for each user to have a file listing the only sender addresses from which she will accept mail? I want to do this so my family members don't get any spam (or other inappropriate mail).

A0716:  Let's assume each user has a file called .acceptlist in the home directory. You can put in your ACL a line like this:

   require senders = /home/$local_part/.acceptlist

This will reject RCPT commands when the sender is not in the accept list for the recipient. (Replace /home/$local_part with whatever the correct path to your user's home directories is.)

One problem with this is that it will block bounce messages, which have empty senders. You can get round this, by changing the line to this:

   require senders =  : /home/$local_part/.acceptlist

However, this will, of course, let in spam that has a null sender.

Q0717:  When using Nessus on a system that runs Exim, a number of security issues are raised. Nessus complains that Exim answers to EXPN and/or VRFY; sometimes it even complains that Exim allows relaying.

A0717:  Exim supports EXPN and VRFY only if you permit it to do so in the ACLs defined by acl_smtp_expn and acl_smtp_vrfy, respectively. Otherwise, its responses are

   550 Administrative prohibition
   252 Administrative prohibition

Maybe the use of 252 is the “problem”. It is recommended that this be done (by those that discuss these things) because there are stupid clients that attempt VRFY before sending a message.

Q0718:  Could anyone points me to right rules to prevent sending/receiving messages to/for domains which have one MX to localhost or only have address 127.0.0.1 ?

A0718:  See Q0319.

Q0719:  I would like to have a per-user limit for the maximum size of messages that can be sent.

A0719:  The simplest way to do this is to put something in a system filter along these lines:

   if $message_size is above
     "${lookup{$sender_address}lsearch{/some/file}{$value}{10M}}"
   then
     fail "Message is larger than $sender_address is allowed to send"
   endif

In practice, an additional check that the message has arrived from your local host or local network is probably wise because sender addresses are easily forged.

Q0720:  I set accept hosts=192.168.122.96/32 in order to accept mail for relaying from my local LAN, but it doesn't work. What's wrong?

A0720:  192.168.122.96/32 is not a network, it is a single host. Exim uses CIDR notation for specifying networks, where the number after the slash is the number of bits in the IP address that must match. Your setting says “32 bits must match”. If you really mean to specify ``the next 32 IP addresses'', you need 192.168.122.96/27.

Q0721:  I have POP-before-SMTP set up on my Exim server, but some clients use Outlook Express, which sends queued messages before checking the mailbox, so it doesn't work.

A0721:  Implement SMTP authentication.

Q0722:  I installed Amavis and it is working, but bounces are simply vanishing.

A0722:  Check that you haven't inadvertently set up the transport like this:

   amavis:
     driver = pipe
     command = "/usr/sbin/amavis -f ${sender_address} -d ${pipe_addresses}"

The last line should be:

   command = /usr/sbin/amavis -f <$sender_address> -d $pipe_addresses

The important thing is the <> around the sender address; removal of the unnecessary "" and {} is just tidying. See the amavis FAQ at http://www.amavis.org/amavis-faq.php3.

Q0723:  I can't get Pine to work with PLAIN authentication; Exim keeps responding "535 Incorrect authentication data".

A0723:  You need to have this setting in your PLAIN authenticator:

   server_prompts = :

This is missing in the examples in all but the most recent Exim documentation, because it was not realized that PLAIN authentication could be requested by a client without sending the data with the request. If the data is not sent, an empty prompt is expected.

Q0724:  I have used :fail: in some aliases; when one of these addresses is refused, I see the message on the log, but the response to the remote user is “unknown user” instead of the message from the alias file. How can I change this?

A0724:  Have you got a message qualifier in the relevant ACL? Exim uses the message line in the ACL in preference to the message returned by the router. This is so you can restrict the amount of information that “escapes” from your site via SMTP if you want to. Remove the message line in the ACL entry that has verify = recipient and your message will get through.

Alternatively, if you are running Exim 4.10 or later, you can use the $acl_verify_message variable in your message to include the message from the router. See also Q0725.

Q0725:  I've set up some specific rejection messages for certain recipients, but when I test them, the SMTP message is always 550 5.1.1 <user@mydomain.com>... User unknown.

A0725:  That is not an Exim message (the “5.1.1” is a clue; Exim doesn't use those extended codes). You are probably being defeated by software that sees the 550 error code, and insists on putting in its own text. There is stupid software that does this. You can test Exim by using -bh or making a telnet call to the SMTP port. That way, there's no other software intervening.

Q0726:  My SMTP authentication can be bypassed by sending an unknown user name and an empty password. What is wrong with this condition in a PLAIN authenticator?

   server_condition = ${if eq{$2} {${lookup mysql{SELECT password FROM \
     accounts WHERE username='${local_part:$1}'}}}{1}{0}}

A0726:  Your lookup item returns an empty string when the user does not exist. You should instead arrange for the lookup to fail:

   server_condition = ${if eq{$2} {${lookup mysql{SELECT password FROM \
     accounts WHERE username='${local_part:$1}'}{$value}fail}}{1}{0}}

Q0727:  When a message has many recipients, how can I stop SpamAssassin from being called for each of them? I'm running it from a pipe transport.

A0727:  In the transport configuration, set batch_max to a value greater than one.

Q0728:  How do I use Exiscan, SA-Exim, SpamAssassin, Clam Antivirus, Sophos SAVI, or sophie with Exim?

A0728:  There's a mini-HOWTO about these available via http://www.timj.co.uk/linux/exim.php. See also sample configuration C047.

Q0729:  How can I screen out addresses that are neither valid usernames or distribution lists on mail being forwarded to an internal Win2K server?

A0729:  A user suggested using a router like this to do the recipient verification:

   verify_user_router:
      driver = accept
      domains = win2kdomain.com
      local_parts=\
        ldap;user="cn=ldap-guest,cn=Users,dc=win2kdomain,dc=com"\
        pass=guest \
        ldap:://win2kpdc/dc=win2kdomain,dc=com?mailNickname?\
        sub?(&(mailNickname=$local_part)\
        (showInAddressBook=*)(sAMAccountName=*))
      verify_only

Set up ldap-guest as a normal domain user on the Win2K PDC.

Also, you need to set no_verify on all the other routers that handle that domain.

Q0730:  How can I use the same passwords for SMTP authentication as I use for Courier IMAP access to my server?

A0730:  You can access the Courier authdaemon from an Exim authenticator. You must arrange for the Exim user (often exim but sometimes mail) to be able to access /var/run/courier/authdaemon/socket. The configuration is something of a hack, but it is reported to work. Here is a LOGIN authenticator:

   login:
     driver = plaintext
     public_name = LOGIN
     server_prompts = Username:: : Password::
     server_condition = \
       ${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
       {AUTH 76\n${length_76:exim\nlogin\n$1\n$2\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n}}}}{FAIL\n} {no}{yes}}
     server_set_id = $1

Here is a PLAIN authenticator:

   plain:
     driver = plaintext
     public_name = PLAIN
     server_prompts = :
     server_condition = \
       ${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
       {AUTH 76\n${length_76:exim\nlogin\n$2\n$3\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
       \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n}}}}{FAIL\n} {no}{yes}}
     server_set_id = $2

Q0731:  Is there any defence I can use against spam sent through an open proxy?

A0731:  The ident feature can be used in some cases. See the discussion in Q5023.

Q0732:  I would like to either warn or deny when a host uses an underscore in the EHLO command.

A0732:  First, set

   helo_allow_chars = _

This tells Exim not to reject the EHLO or HELO command immediately. Once you have done that, you can test for the underscore in an ACL. For example, to log a warning for hosts in your LAN, and reject for other hosts, you could do something like this:

   deny  message = Underscores are not valid in host names
         hosts = ! +lan_hosts
         condition = ${if match{$sender_helo_name}{_}{yes}{no}}
   warn  log_message = Accepted underscore from [$sender_host_address]
         condition = ${if match{$sender_helo_name}{_}{yes}{no}}

Q0733:  Is there any way to tell Exim not to lookup the IP address against any DNS black list if the connection is over IPv6?

A0733:  Use this condition in your ACL:

   condition = ${if match{${mask:$sender_host_address/0}}\
                {${mask:::0/0}}{no}{yes}}

From Exim 4.23 onwards, this can be simplified to

   condition = ${if isip6{$sender_host_address}{no}{yes}}

Q0734:  How do MailScanner and Exiscan compare? What are the pros and cons?

A0734:  The big advantage of Exiscan is that it can reject messages at SMTP time before you have accepted responsibility for them, which means you don't have to deal with bouncing messages and thereby becoming a collateral spammer.

The big advantage of MailScanner is that it gives you much greater control over the load on your machines. You configure it according to the maximum processing capacity of your computer and it will not exceed that; in fact because it deals with messages in batches the cost of processing a message actually goes down slightly as the load increases, because the per-batch costs are shared by more messages.

With Exiscan, you have to rely on Exim's load protection mechanisms, which basically means that you have to stop accepting messages when your machine gets too loaded. This is bad if the machine happens to be an SMTP smarthost. You therefore need more overcapacity with Exiscan than with MailScanner.

Q0735:  How can I block non-FQDNs in HELO/EHLOs?

A0735:  Many workstation clients send single-component names; take care that you do not block legitimate mail. With that proviso, you can do it using something like this in an ACL:

 	 drop  message = HELO doesn't look like a hostname
	       log_message = Not a hostname
	       condition = ${if match{$sender_helo_name} \
				{\N^[^.].*\.[^.]+$\N}{no}{yes}}

This means: Drop the HELO unless it contains a dot somewhere in the HELO string, but the string may not begin or end with a dot. Thus, the imposed minimum length is 3 characters.

The data for HELO/EHLO doesn't have to be a host name; it may legitimately be an IP address literal instead. The above test succeeds with an IPv4 address literal, but if you want also to accept IPv6 address literals, you will have to modify the regular expression.

Q0736:  Is it possible to tell exim to drop the connection after a server attempts to send a message to a number of unknown users?

A0736:  Yes. Use $rcpt_fail_count and the drop ACL command, as in this example:

   drop  message = Too many unknown users
         condition = ${if >{$rcpt_fail_count}{15}{yes}{no}}

Q0737:  Is there some way to tell Exim not to consider 127.0.0.1 as a valid MX?

A0737:  See Q0319.

Q0738:  How can I configure Exim to delay the SMTP connection if more than 10 invalid recipients are received in one message?

A0738:  Put something like this in your RCPT ACL:

   deny  message         = Max $rcpt_fail_count failed recipients allowed
         condition       = ${if >{$rcpt_fail_count}{10} {1}}
         ! verify        = recipient
         delay           = ${eval: $rcpt_fail_count * 10}s
         log_message     = $rcpt_fail_count failed recipient attempts

This example increases the delay for each failed recipient.

Q0739:  Does Exim support SPF?

A0739:  An Exim ACL can be used. See http://spf.pobox.com/downloads.html.



Contents   Previous   Next