Previous   Next   Contents       (Exim 4.50 Specification)

39. Access control lists

Access Control Lists (ACLs) are defined in a separate section of the run time configuration file, headed by “begin acl”. Each ACL definition starts with a name, terminated by a colon. Here is a complete ACL section that contains just one very small ACL:

  begin acl
  
  small_acl:
    accept   hosts = one.host.only

You can have as many lists as you like in the ACL section, and the order in which they appear does not matter. The lists are self-terminating.

The majority of ACLs are used to control Exim's behaviour when it receives certain SMTP commands. This applies both to incoming TCP/IP connections, and when a local process submits a message using SMTP by specifying the -bs option. The most common use is for controlling which recipients are accepted in incoming messages. In addition, you can define an ACL that is used to check local non-SMTP messages. The default configuration file contains an example of a realistic ACL for checking RCPT commands. This is discussed in chapter 7.

39.1 Testing ACLs

The -bh command line option provides a way of testing your ACL configuration locally by running a fake SMTP session with which you interact. The host relay-test.mail-abuse.org provides a service for checking your relaying configuration (see section 39.37 for more details).

39.2 Specifying when ACLs are used

In order to cause an ACL to be used, you have to name it in one of the relevant options in the main part of the configuration. These options are:

  acl_not_smtp  ACL for non-SMTP messages
  acl_smtp_auth  ACL for AUTH
  acl_smtp_connect  ACL for start of SMTP connection
  acl_smtp_data  ACL after DATA is complete
  acl_smtp_etrn  ACL for ETRN
  acl_smtp_expn  ACL for EXPN
  acl_smtp_helo  ACL for HELO or EHLO
  acl_smtp_mail  ACL for MAIL
  acl_smtp_mailauth  ACL for the AUTH parameter of MAIL
  acl_smtp_mime  ACL for content-scanning MIME parts
  acl_smtp_predata  ACL at start of DATA command
  acl_smtp_quit  ACL for QUIT
  acl_smtp_rcpt  ACL for RCPT
  acl_smtp_starttls  ACL for STARTTLS
  acl_smtp_vrfy  ACL for VRFY

For example, if you set

  acl_smtp_rcpt = small_acl

the little ACL defined above is used whenever Exim receives a RCPT command in an SMTP dialogue. The majority of policy tests on incoming messages can be done when RCPT commands arrive. A rejection of RCPT should cause the sending MTA to give up on the recipient address contained in the RCPT command, whereas rejection at other times may cause the client MTA to keep on trying to deliver the message. It is therefore recommended that you do as much testing as possible at RCPT time.

39.3 The non-SMTP ACL

The non-SMTP ACL applies to all non-interactive incoming messages, that is, it applies to batch SMTP as well as to non-SMTP messages. (Batch SMTP is not really SMTP.) This ACL is run just before the local_scan() function. Any kind of rejection is treated as permanent, because there is no way of sending a temporary error for these kinds of message. Many of the ACL conditions (for example, host tests, and tests on the state of the SMTP connection such as encryption and authentication) are not relevant and are forbidden in this ACL.

39.4 The connect ACL

The ACL test specified by acl_smtp_connect happens after the test specified by host_reject_connection (which is now an anomaly) and any TCP Wrappers testing (if configured).

39.5 The DATA ACLs

Two ACLs are associated with the DATA command, because it is two-stage command, with two responses being sent to the client. When the DATA command is received, the ACL defined by acl_smtp_predata is obeyed. This gives you control after all the RCPT commands, but before the message itself is received. It offers the opportunity to give a negative response to the DATA command before the data is transmitted. Header lines added by MAIL or RCPT ACLs are not visible at this time, but any that are defined here are visible when the acl_smtp_data ACL is run.

You cannot test the contents of the message, for example, to verify addresses in the headers, at RCPT time or when the DATA command is received. Such tests have to appear in the ACL that is run after the message itself has been received, before the final response to the DATA command is sent. This is the ACL specified by acl_smtp_data, which is the second ACL that is associated with the DATA command.

For both of these ACLs, it is not possible to reject individual recipients. An error response rejects the entire message. Unfortunately, it is known that some MTAs do not treat hard (5xx) responses to the DATA command (either before or after the data) correctly – they keep the message on their queues and try again later, but that is their problem, though it does waste some of your resources.

39.6 The MIME ACL

The acl_smtp_mime option is available only when Exim is compiled with the content-scanning extension. For details, see chapter 40.

39.7 The QUIT ACL

The ACL for the SMTP QUIT command is anomalous, in that the outcome of the ACL does not affect the response code to QUIT, which is always 221. Thus, the ACL does not in fact control any access. For this reason, the only verbs that are permitted are accept and warn.

This ACL can be used for tasks such as custom logging at the end of an SMTP session. For example, you can use ACL variables in other ACLs to count messages, recipients, etc., and log the totals at QUIT time using one or more logwrite modifiers on a warn verb.

You do not need to have a final accept, but if you do, you can use a message modifier to specify custom text that is sent as part of the 221 response to QUIT.

This ACL is run only for a “normal” QUIT. For certain kinds of disastrous failure (for example, failure to open a log file, or when Exim is bombing out because it has detected an unrecoverable error), all SMTP commands from the client are given temporary error responses until QUIT is received or the connection is closed. In these special cases, the QUIT ACL does not run.

39.8 Finding an ACL to use

The value of an acl_smtp_xxx option is expanded before use, so you can use different ACLs in different circumstances. The resulting string does not have to be the name of an ACL in the configuration file; there are other possibilities. Having expanded the string, Exim searches for an ACL as follows:

39.9 ACL return codes

Except for the QUIT ACL, which does not affect the SMTP return code (see section 39.7 above), the result of running an ACL is either “accept” or “deny”, or, if some test cannot be completed (for example, if a database is down), “defer”. These results cause 2xx, 5xx, and 4xx return codes, respectively, to be used in the SMTP dialogue. A fourth return, “error”, occurs when there is an error such as invalid syntax in the ACL. This also causes a 4xx return code.

For the non-SMTP ACL, “defer” and “error” are treated in the same way as “deny”, because there is no mechanism for passing temporary errors to the submitters of non-SMTP messages.

ACLs that are relevant to message reception may also return “discard”. This has the effect of “accept”, but causes either the entire message or an individual recipient address to be discarded. In other words, it is a blackholing facility. Use it with care.

If the ACL for MAIL returns “discard”, all recipients are discarded, and no ACL is run for subsequent RCPT commands. The effect of “discard” in a RCPT ACL is to discard just the one recipient address. If there are no recipients left when the message's data is received, the DATA ACL is not run. A “discard” return from the DATA or the non-SMTP ACL discards all the remaining recipients. The “discard” return is not permitted for the acl_smtp_predata ACL.

The local_scan() function is always run, even if there are no remaining recipients; it may create new recipients.

39.10 Unset ACL options

The default actions when any of the acl_xxx options are unset are not all the same. Note: These defaults apply only when the relevant ACL is not defined at all. For any defined ACL, the default action when control reaches the end of the ACL statements is “deny”.

For acl_not_smtp, acl_smtp_auth, acl_smtp_connect, acl_smtp_data, acl_smtp_helo, acl_smtp_mail, acl_smtp_mailauth, acl_smtp_mime, acl_smtp_predata, acl_smtp_quit, and acl_smtp_starttls, the action when the ACL is not defined is “accept”.

For the others (acl_smtp_etrn, acl_smtp_expn, acl_smtp_rcpt, and acl_smtp_vrfy), the action when the ACL is not defined is “deny”. This means that acl_smtp_rcpt must be defined in order to receive any messages over an SMTP connection. For an example, see the ACL in the default configuration file.

39.11 Data for message ACLs

When a MAIL or RCPT ACL, or either of the DATA ACLs, is running, the variables that contain information about the host and the message's sender (for example, $sender_host_address and $sender_address) are set, and can be used in ACL statements. In the case of RCPT (but not MAIL or DATA), $domain and $local_part are set from the argument address.

When an ACL for the AUTH parameter of MAIL is running, the variables that contain information about the host are set, but $sender_address is not yet set. Section 33.2 contains a discussion of this parameter and how it is used.

The $message_size variable is set to the value of the SIZE parameter on the MAIL command at MAIL, RCPT and pre-data time, or to -1 if that parameter is not given. The value is updated to the true message size by the time the final DATA ACL is run (after the message data has been received).

The $rcpt_count variable increases by one for each RCPT command received. The $recipients_count variable increases by one each time a RCPT command is accepted, so while an ACL for RCPT is being processed, it contains the number of previously accepted recipients. At DATA time (for both the DATA ACLs), $rcpt_count contains the total number of RCPT commands, and $recipients_count contains the total number of accepted recipients.

39.12 Data for non-message ACLs

When an ACL is being run for AUTH, EHLO, ETRN, EXPN, HELO, STARTTLS, or VRFY, the remainder of the SMTP command line is placed in $smtp_command_argument. This can be tested using a condition condition. For example, here is an ACL for use with AUTH, which insists that either the session is encrypted, or the CRAM-MD5 authentication method is used. In other words, it does not permit authentication methods that use cleartext passwords on unencrypted connections.

  acl_check_auth:
    accept encrypted = *
    accept condition = ${if eq{${uc:$smtp_command_argument}}\
                       {CRAM-MD5}}
    deny   message   = TLS encryption or CRAM-MD5 required

(Another way of applying this restriction is to arrange for the authenticators that use cleartext passwords not to be advertised when the connection is not encrypted. You can use the generic server_advertise_condition authenticator option to do this.)

39.13 Format of an ACL

An individual ACL consists of a number of statements. Each statement starts with a verb, optionally followed by a number of conditions and “modifiers”. Modifiers can change the way the verb operates, define error and log messages, set variables, insert delays, and vary the processing of accepted messages.

If all the conditions are met, the verb is obeyed. The same condition may be used (with different arguments) more than once in the same statement. This provides a means of specifying an “and” conjunction between conditions. For example:

  deny  dnslists = list1.example
        dnslists = list2.example

If there are no conditions, the verb is always obeyed. Exim stops evaluating the conditions and modifiers when it reaches a condition that fails. What happens then depends on the verb (and in one case, on a special modifier). Not all the conditions make sense at every testing point. For example, you cannot test a sender address in the ACL that is run for a VRFY command.

39.14 ACL verbs

The ACL verbs are as follows:

At the end of each ACL there is an implicit unconditional deny.

As you can see from the examples above, the conditions and modifiers are written one to a line, with the first one on the same line as the verb, and subsequent ones on following lines. If you have a very long condition, you can continue it onto several physical lines by the usual backslash continuation mechanism. It is conventional to align the conditions vertically.

39.15 ACL variables

There are some special variables that can be set during ACL processing. They can be used to pass information between different ACLs, different invocations of the same ACL in the same SMTP connection, and between ACLs and the routers, transports, and filters that are used to deliver a message. There are two sets of these variables:

When a message is accepted, the current values of all the ACL variables are preserved with the message and are subsequently made available at delivery time. The ACL variables are set by modifier called set. For example:

  accept hosts = whatever
         set acl_m4 = some value

Note: a leading dollar sign is not used when naming a variable that is to be set. If you want to set a variable without taking any action, you can use a warn verb without any other modifiers or conditions.

39.16 Condition and modifier processing

An exclamation mark preceding a condition negates its result. For example,

  deny   domains = *.dom.example
        !verify = recipient

causes the ACL to return “deny” if the recipient domain ends in dom.example and the recipient address cannot be verified.

The arguments of conditions and modifiers are expanded. A forced failure of an expansion causes a condition to be ignored, that is, it behaves as if the condition is true. Consider these two statements:

  accept  senders = ${lookup{$host_name}lsearch\
                    {/some/file}{$value}fail}
  accept  senders = ${lookup{$host_name}lsearch\
                    {/some/file}{$value}{}}

Each attempts to look up a list of acceptable senders. If the lookup succeeds, the returned list is searched, but if the lookup fails the behaviour is different in the two cases. The fail in the first statement causes the condition to be ignored, leaving no further conditions. The accept verb therefore succeeds. The second statement, however, generates an empty list when the lookup fails. No sender can match an empty list, so the condition fails, and therefore the accept also fails.

ACL modifiers appear mixed in with conditions in ACL statements. Some of them specify actions that are taken as the conditions for a statement are checked; others specify text for messages that are used when access is denied or a warning is generated. The control modifier affects the way an incoming message is handled.

The positioning of the modifiers in an ACL statement important, because the processing of a verb ceases as soon as its outcome is known. Only those modifiers that have already been encountered will take effect. For example, consider this use of the message modifier:

  require message = Can't verify sender
          verify = sender
          message = Can't verify recipient
          verify = recipient
          message = This message cannot be used

If sender verification fails, Exim knows that the result of the statement is “deny”, so it goes no further. The first message modifier has been seen, so its text is used as the error message. If sender verification succeeds, but recipient verification fails, the second message is used. If recipient verification succeeds, the third message becomes “current”, but is never used because there are no more conditions to cause failure.

For the deny verb, on the other hand, it is always the last message modifier that is used, because all the conditions must be true for rejection to happen. Specifying more than one message modifier does not make sense, and the message can even be specified after all the conditions. For example:

  deny   hosts = ...
        !senders = *@my.domain.example
         message = Invalid sender from client host

The “deny” result does not happen until the end of the statement is reached, by which time Exim has set up the message.

39.17 ACL modifiers

The ACL modifiers are as follows:


control = <text>

This modifier affects the subsequent processing of the SMTP connection or of an incoming message that is accepted. The effect of the first type of control lasts for the duration of the connection, whereas the effect of the second type lasts only until the current message has been received. The message-specific controls always apply to the whole message, not to individual recipients, even if the control modifier appears in a RCPT ACL.

As there are now quite a few controls that can be applied, they are described separately in section 39.18. The control modifier can be used in several different ways. For example:


delay = <time>

This modifier causes Exim to wait for the time interval before proceeding. The time is given in the usual Exim notation. This modifier may appear in any ACL. The delay happens as soon as the modifier is processed. However, when testing Exim using the -bh option, the delay is not actually imposed (an appropriate message is output instead).

Like control, delay can be used with accept or deny, for example:

  deny    ...some conditions...
          delay = 30s

The delay happens if all the conditions are true, before the statement returns “deny”. Compare this with:

  deny    delay = 30s
          ...some conditions...

which waits for 30s before processing the conditions. The delay modifier can also be used with warn and together with control:

  warn ...some conditions...
   delay = 2m
   control = freeze
  accept ...


endpass

This modifier, which has no argument, is recognized only in accept statements. It marks the boundary between the conditions whose failure causes control to pass to the next statement, and the conditions whose failure causes the ACL to return “deny”. See the description of accept above.


log_message = <text>

This modifier sets up a message that is used as part of the log message if the ACL denies access or a warn statement's conditions are true. For example:

  require log_message = wrong cipher suite $tls_cipher
          encrypted   = DES-CBC3-SHA

log_message adds to any underlying error message that may exist because of the condition failure. For example, while verifying a recipient address, a :fail: redirection might have already set up a message. Although the message is usually defined before the conditions to which it applies, the expansion does not happen until Exim decides that access is to be denied. This means that any variables that are set by the condition are available for inclusion in the message. For example, the $dnslist_<xxx> variables are set after a DNS black list lookup succeeds. If the expansion of log_message fails, or if the result is an empty string, the modifier is ignored.

If you want to use a warn statement to log the result of an address verification, you can use $acl_verify_message to include the verification error message.

If log_message is used with a warn statement, “Warning:” is added to the start of the logged message. If the same warning log message is requested more than once while receiving a single email message, only one copy is actually logged. If you want to log multiple copies, use logwrite instead of log_message. In the absence of log_message and logwrite, nothing is logged for a succesful warn statement.

If log_message is not present and there is no underlying error message (for example, from the failure of address verification), but message is present, the message text is used for logging rejections. However, if any text for logging contains newlines, only the first line is logged. In the absence of both log_message and message, a default built-in message is used for logging rejections.


logwrite = <text>

This modifier writes a message to a log file as soon as it is encountered when processing an ACL. (Compare log_message, which, except in the case of warn, is used only if the ACL statement denies access.) The logwrite modifier can be used to log special incidents in ACLs. For example:

  accept <some special conditions>
   control = freeze
   logwrite = froze message because ...

By default, the message is written to the main log. However, it may begin with a colon, followed by a comma-separated list of log names, and then another colon, to specify exactly which logs are to be written. For example:

  logwrite = :main,reject: text for main and reject logs
  logwrite = :panic: text for panic log only

message = <text>

This modifier sets up a text string that is expanded and used as an error message if the current statement causes the ACL to deny access. The expansion happens at the time Exim decides that access is to be denied, not at the time it processes message. If the expansion fails, or generates an empty string, the modifier is ignored. For ACLs that are triggered by SMTP commands, the message is returned as part of the SMTP error response.

The message modifier is also used with the warn verb to specify one or more header lines to be added to an incoming message when all the conditions are true. See section 39.19 for more details. If message is used with warn in an ACL that is not concerned with receiving a message, it has no effect.

The text is literal; any quotes are taken as literals, but because the string is expanded, backslash escapes are processed anyway. If the message contains newlines, this gives rise to a multi-line SMTP response. Like log_message, the contents of message are not expanded until after a condition has failed.

If message is used on a statement that verifies an address, the message specified overrides any message that is generated by the verification process. However, the original message is available in the variable $acl_verify_message, so you can incorporate it into your message if you wish. In particular, if you want the text from :fail: items in redirect routers to be passed back as part of the SMTP response, you should either not use a message modifier, or make use of $acl_verify_message.


set <acl_name> = <value>

This modifier puts a value into one of the ACL variables (see section 39.15).



39.18 Use of the