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.
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).
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.
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.
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).
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.
The acl_smtp_mime option is available only when Exim is compiled with the content-scanning extension. For details, see chapter 40.
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.
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:
If the string begins with a slash, Exim uses it as a file name, and reads its contents as an ACL. The lines are processed in the same way as lines in the Exim configuration file. In particular, continuation lines are supported, blank lines are ignored, as are lines whose first non-whitespace character is #. If the file does not exist or cannot be read, an error occurs (typically causing a temporary failure of whatever caused the ACL to be run). For example:
acl_smtp_data = /etc/acls/\
${lookup{$sender_host_address}lsearch\
{/etc/acllist}{$value}{default}}
This looks up an ACL file to use on the basis of the host's IP address, falling back to a default if the lookup fails. If an ACL is successfully read from a file, it is retained in memory for the duration of the Exim process, so that it can be re-used without having to re-read the file.
If the string does not start with a slash, and does not contain any spaces, Exim searches the ACL section of the configuration for an ACL whose name matches the string.
If no named ACL is found, or if the string contains spaces, Exim parses the string as an inline ACL. This can save typing in cases where you just want to have something like
acl_smtp_vrfy = accept
in order to allow free use of the VRFY command. Such a string may contain newlines; it is processed in the same way as an ACL that is read from a file.
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 loc
9. File and database lookups
Exim can be configured to look up data in files or databases as it processes messages. Two different kinds of syntax are used:
A string that is to be expanded may contain explicit lookup requests. These cause parts of the string to be replaced by data that is obtained from the lookup.
Lists of domains, hosts, and email addresses can contain lookup requests as a way of avoiding excessively long linear lists. In this case, the data that is returned by the lookup is often (but not always) discarded; whether the lookup succeeds or fails is what really counts. These kinds of list are described in chapter 10.
It is easy to confuse the two different kinds of lookup, especially as the lists that may contain the second kind are always expanded before being processed as lists. Therefore, they may also contain lookups of the first kind. Be careful to distinguish between the following two examples:
domains = ${lookup{$sender_host_address}lsearch{/some/file}}
domains = lsearch;/some/file
The first uses a string expansion, the result of which must be a domain list. String expansions are described in detail in chapter 11. The expansion takes place first, and the file that is searched could contain lines like this:
192.168.3.4: domain1 : domain2 : ... 192.168.1.9: domain3 : domain4 : ...
Thus, the result of the expansion is a list of domains (and possibly other types of item that are allowed in domain lists).
In the second case, the lookup is a single item in a domain list. It causes Exim to use a lookup to see if the domain that is being processed can be found in the file. The file could contains lines like this:
domain1: domain2:
Any data that follows the keys is not relevant when checking that the domain matches the list item.
It is possible to use both kinds of lookup at once. Consider a file containing lines like this:
192.168.5.6: lsearch;/another/file
If the value of $sender_host_address is 192.168.5.6, expansion of the first domains setting above generates the second setting, which therefore causes a second lookup to occur.
The rest of this chapter describes the different lookup types that are available. Any of them can be used in either of the circumstances described above. The syntax requirements for the two cases are described in chapters 11 and 10, respectively.
Two different styles of data lookup are implemented:
The single-key style requires the specification of a file in which to look, and a single key to search for. The key must be a non-empty string for the lookup to succeed. The lookup type determines how the file is searched.
The query style accepts a generalized database query. No particular key value is assumed by Exim for query-style lookups. You can use whichever Exim variable(s) you need to construct the database query.
The code for each lookup type is in a separate source file that is included in the binary of Exim only if the corresponding compile-time option is set. The default settings in src/EDITME are:
LOOKUP_DBM=yes LOOKUP_LSEARCH=yes
which means that only linear searching and DBM lookups are included by default. For some types of lookup (e.g. SQL databases), you need to install appropriate libraries and header files before building Exim.
The following single-key lookup types are implemented:
cdb: The given file is searched as a Constant DataBase file, using the key string without a terminating binary zero. The cdb format is designed for indexed files that are read frequently and never updated, except by total re-creation. As such, it is particulary suitable for large files containing aliases or other indexed data referenced by an MTA. Information about cdb can be found in several places:
http://www.pobox.com/~djb/cdb.html
ftp://ftp.corpit.ru/pub/tinycdb/
http://packages.debian.org/stable/utils/freecdb.html
A cdb distribution is not needed in order to build Exim with cdb support, because the code for reading cdb files is included directly in Exim itself. However, no means of building or testing cdb files is provided with Exim, so you need to obtain a cdb distribution in order to do this.
dbm: Calls to DBM library functions are used to extract data from the given DBM file by looking up the record with the given key. A terminating binary zero is included in the key that is passed to the DBM library. See section 4.3 for a discussion of DBM libraries. For all versions of Berkeley DB, Exim uses the DB_HASH style of database when building DBM files using the exim_dbmbuild utility. However, when using Berkeley DB versions 3 or 4, it opens existing databases for reading with the DB_UNKNOWN option. This enables it to handle any of the types of database that the library supports, and can be useful for accessing DBM files created by other applications. (For earlier DB versions, DB_HASH is always used.)
dbmnz: This is the same as dbm, except that a terminating binary zero is not included in the key that is passed to the DBM library. You may need this if you want to look up data in files that are created by or shared with some other application that does not use terminating zeros. For example, you need to use dbmnz rather than dbm if you want to authenticate incoming SMTP calls using the passwords from Courier's /etc/userdbshadow.dat file. Exim's utility program for creating DBM files (exim_dbmbuild) includes the zeros by default, but has an option to omit them (see section 49.9).
dsearch: The given file must be a directory; this is searched for a file whose name is the key. The key may not contain any forward slash characters. The result of a successful lookup is the name of the file. An example of how this lookup can be used to support virtual domains is given in section 46.6.
iplsearch: The given file is a text file containing keys and data. A key is terminated by a colon or white space or the end of the line. The keys in the file must be IP addresses, or IP addresses with CIDR masks. Keys that involve IPv6 addresses must be enclosed in quotes to prevent the first internal colon being interpreted as a key terminator. For example:
1.2.3.4: data for 1.2.3.4 192.168.0.0/16 data for 192.168.0.0/16 "abcd::cdab": data for abcd::cdab "abcd:abcd::/32" data for abcd:abcd::/32
The key for an iplsearch lookup must be an IP address (without a mask). The file is searched linearly, using the CIDR masks where present, until a matching key is found. The first key that matches is used; there is no attempt to find a best match. Apart from the way the keys are matched, the processing for iplsearch is the same as for lsearch.
Warning 1: Unlike most other single-key lookup types, a file of data for iplsearch can not be turned into a DBM or cdb file, because those lookup types support only literal keys.
Warning 2: In a host list, you must always use net-iplsearch so that the implicit key is the host's IP address rather than its name (see section 10.12).
lsearch: The given file is a text file that is searched linearly for a line beginning with the search key, terminated by a colon or white space or the end of the line. The first occurrence that is found in the file is used. White space between the key and the colon is permitted. The remainder of the line, with leading and trailing white space removed, is the data. This can be continued onto subsequent lines by starting them with any amount of white space, but only a single space character is included in the data at such a junction. If the data begins with a colon, the key must be terminated by a colon, for example:
baduser: :fail:
Empty lines and lines beginning with # are ignored, even if they occur in the middle of an item. This is the traditional textual format of alias files. Note that the keys in an lsearch file are literal strings. There is no wildcarding of any kind.
In most lsearch files, keys are not required to contain colons or # characters, or whitespace. However, if you need this feature, it is available. If a key begins with a doublequote character, it is terminated only by a matching quote (or end of line), and the normal escaping rules apply to its contents (see section 6.12). An optional colon is permitted after quoted keys (exactly as for unquoted keys). There is no special handling of quotes for the data part of an lsearch line.
nis: The given file is the name of a NIS map, and a NIS lookup is done with the given key, without a terminating binary zero. There is a variant called nis0 which does include the terminating binary zero in the key. This is reportedly needed for Sun-style alias files. Exim does not recognize NIS aliases; the full map names must be used.
wildlsearch or nwildlsearch: These search a file linearly, like lsearch, but instead of being interpreted as a literal string, each key may be wildcarded. The difference between these two lookup types is that for wildlsearch, each key in the file is string-expanded before being used, whereas for nwildlsearch, no expansion takes place.
Like lsearch, the testing is done case-insensitively. The following forms of wildcard are recognized:
The string may begin with an asterisk to mean ends with. For example:
*.a.b.c data for anything.a.b.c *fish data for anythingfish
The string may begin with a circumflex to indicate a regular expression. For example, for wildlsearch:
^\N\d+\.a\.b\N data for <digits>.a.b
Note the use of \N to disable expansion of the contents of the regular expression. If you are using nwildlsearch, where the keys are not string-expanded, the equivalent entry is:
^\d+\.a\.b data for <digits>.a.b
If the regular expression contains white space or colon characters, you must either quote it (see lsearch above), or represent these characters in other ways. For example, \s can be used for white space and \x3A for a colon. This may be easier than quoting, because if you quote, you have to escape all the backslashes inside the quotes.
Although I cannot see it being of much use, the general matching function that is used to implement (n)wildlsearch means that the string may begin with a lookup name terminated by a semicolon, and followed by lookup data. For example:
cdb;/some/file data for keys that match the file
The data that is obtained from the nested lookup is discarded.
Keys that do not match any of these patterns are interpreted literally. The continuation rules for the data are the same as for lsearch, and keys may be followed by optional colons.
Warning: Unlike most other single-key lookup types, a file of data for (n)wildlsearch can not be turned into a DBM or cdb file, because those lookup types support only literal keys.
The supported query-style lookup types are listed below. Further details about many of them are given in later sections.
dnsdb: This does a DNS search for one or more records whose domain names are given in the supplied query. The resulting data is the contents of the records. See section 9.9.
ibase: This does a lookup in an Interbase database.
ldap: This does an LDAP lookup using a query in the form of a URL, and returns attributes from a single entry. There is a variant called ldapm that permits values from multiple entries to be returned. A third variant called ldapdn returns the Distinguished Name of a single entry instead of any attribute values. See section 9.11.
mysql: The format of the query is an SQL statement that is passed to a MySQL database. See section 9.18.
nisplus: This does a NIS+ lookup using a query that can specify the name of the field to be returned. See section 9.17.
oracle: The format of the query is an SQL statement that is passed to an Oracle database. See section 9.18.
passwd is a query-style lookup with queries that are just user names. The lookup calls getpwnam() to interrogate the system password data, and on success, the result string is the same as you would get from an lsearch lookup on a traditional /etc/passwd file, though with * for the password value. For example:
*:42:42:King Rat:/home/kr:/bin/bash
pgsql: The format of the query is an SQL statement that is passed to a PostgreSQL database. See section 9.18.
testdb: This is a lookup type that is used for testing Exim. It is not likely to be useful in normal operation.
whoson: Whoson (http://whoson.sourceforge.net) is a proposed Internet protocol that allows Internet server programs to check whether a particular (dynamically allocated) IP address is currently allocated to a known (trusted) user and, optionally, to obtain the identity of the said user. In Exim, this can be used to implement POP before SMTP checking using ACL statements such as
require condition = \
${lookup whoson {$sender_host_address}{yes}{no}}
The query consists of a single IP address. The value returned is the name of the authenticated user.
Lookup functions can return temporary error codes if the lookup cannot be completed. For example, a NIS or LDAP database might be unavailable. For this reason, it is not advisable to use a lookup that might do this for critical options such as a list of local domains.
When a lookup cannot be completed in a router or transport, delivery of the message (to the relevant address) is deferred, as for any other temporary error. In other circumstances Exim may assume the lookup has failed, or may give up altogether.
In this context, a default value is a value specified by the administrator that is to be used if a lookup fails.
If * is added to a single-key lookup type (for example, lsearch*) and the initial lookup fails, the key * is looked up in the file to provide a default value. See also the section on partial matching below.
Alternatively, if *@ is added to a single-key lookup type (for example dbm*@) then, if the initial lookup fails and the key contains an @ character, a second lookup is done with everything before the last @ replaced by *. This makes it possible to provide per-domain defaults in alias files that include the domains in the keys. If the second lookup fails (or doesn't take place because there is no @ in the key), * is looked up. For example, a redirect router might contain:
data = ${lookup{$local_part@$domain}lsearch*@{/etc/mixed-aliases}}
Suppose the address that is being processed is jane@eyre.example. Exim looks up these keys, in this order:
jane@eyre.example *@eyre.example *
The data is taken from whichever key it finds first. Note: in an lsearch file, this does not mean the first of these keys in the file. A complete scan is done for each key, and only if it is not found at all does Exim move on to try the next key.
The normal operation of a single-key lookup is to search the file for an exact match with the given key. However, in a number of situations where domains are being looked up, it is useful to be able to do partial matching. In this case, information in the file that has a key starting with *. is matched by any domain that ends with the components that follow the full stop. For example, if a key in a DBM file is
*.dates.fict.example
then when partial matching is enabled this is matched by (amongst others) 2001.dates.fict.example and 1984.dates.fict.example. It is also matched by dates.fict.example, if that does not appear as a separate key in the file.
Note: Partial matching is not available for query-style lookups. It is also not available for any lookup items in address lists (see section 10.18).
Partial matching is implemented by doing a series of separate lookups using keys constructed by modifying the original subject key. This means that it can be used with any of the single-key lookup types, provided that partial matching keys beginning with a special prefix (default *.) are included in the data file. Keys in the file that do not begin with the prefix are matched only by unmodified subject keys when partial matching is in use.
Partial matching is requested by adding the string partial- to the front of the name of a single-key lookup type, for example, partial-dbm. When this is done, the subject key is first looked up unmodified; if that fails, *. is added at the start of the subject key, and it is looked up again. If that fails, further lookups are tried with dot-separated components removed from the start of the subject key, one-by-one, and *. added on the front of what remains.
A minimum number of two non-* components are required. This can be adjusted by including a number before the hyphen in the search type. For example, partial3-lsearch specifies a minimum of three non-* components in the modified keys. Omitting the number is equivalent to partial2-. If the subject key is 2250.dates.fict.example then the following keys are looked up when the minimum number of non-* components is two:
2250.dates.fict.example *.2250.dates.fict.example *.dates.fict.example *.fict.example
As soon as one key in the sequence is successfully looked up, the lookup finishes.
The use of *. as the partial matching prefix is a default that can be changed. The motivation for this feature is to allow Exim to operate with file formats that are used by other MTAs. A different prefix can be supplied in parentheses instead of the hyphen after partial. For example:
domains = partial(.)lsearch;/some/file
In this example, if the domain is a.b.c, the sequence of lookups is a.b.c, .a.b.c, and .b.c (the default minimum of 2 non-wild components is unchanged). The prefix may consist of any punctuation characters other than a closing parenthesis. It may be empty, for example:
domains = partial1()cdb;/some/file
For this example, if the domain is a.b.c, the sequence of lookups is a.b.c, b.c, and c.
If partial0 is specified, what happens at the end (when the lookup with just one non-wild component has failed, and the original key is shortened right down to the null string) depends on the prefix:
If the prefix has zero length, the whole lookup fails.
If the prefix has length 1, a lookup for just the prefix is done. For example, the final lookup for partial0(.) is for . alone.
Otherwise, if the prefix ends in a dot, the dot is removed, and the remainder is looked up. With the default prefix, therefore, the final lookup is for * on its own.
Otherwise, the whole prefix is looked up.
If the search type ends in * or *@ (see section 9.5 above), the search for an ultimate default that this implies happens after all partial lookups have failed. If partial0 is specified, adding * to the search type has no effect with the default prefix, because the * key is already included in the sequence of partial lookups. However, there might be a use for lookup types such as partial0(.)lsearch*.
The use of * in lookup partial matching differs from its use as a wildcard in domain lists and the like. Partial matching works only in terms of dot-separated components; a key such as *fict.example in a database file is useless, because the asterisk in a partial matching subject key is always followed by a dot.
Exim caches all lookup results in order to avoid needless repetition of lookups. However, because (apart from the daemon) Exim operates as a collection of independent, short-lived processes, this caching applies only within a single Exim process. There is no inter-process caching facility.
For single-key lookups, Exim keeps the relevant files open in case there is another lookup that needs them. In some types of configuration this can lead to many files being kept open for messages with many recipients. To avoid hitting the operating system limit on the number of simultaneously open files, Exim closes the least recently used file when it needs to open more files than its own internal limit, which can be changed via the lookup_open_max option.
The single-key lookup files are closed and the lookup caches are flushed at strategic points during delivery for example, after all routing is complete.
When data from an incoming message is included in a query-style lookup, there is the possibility of special characters in the data messing up the syntax of the query. For example, a NIS+ query that contains
[name=$local_part]
will be broken if the local part happens to contain a closing square bracket. For NIS+, data can be enclosed in double quotes like this:
[name="$local_part"]
but this still leaves the problem of a double quote in the data. The rule for NIS+ is that double quotes must be doubled. Other lookup types have different rules, and to cope with the differing requirements, an expansion operator of the following form is provided:
${quote_<lookup-type>:<string>}
For example, the safest way to write the NIS+ query is
[name="${quote_nisplus:$local_part}"]
See chapter 11 for full coverage of string expansions. The quote operator can be used for all lookup types, but has no effect for single-key lookups, since no quoting is ever needed in their key strings.
The dnsdb lookup type uses the DNS as its database. A simple query consists of a record type and a domain name, separated by an equals sign. For example, an expansion string could contain:
${lookup dnsdb{mx=a.b.example}{$value}fail}
The supported DNS record types are A, CNAME, MX, NS, PTR, SRV, and TXT, and,
when Exim is compiled with IPv6 support, AAAA (and A6 if that is also
configured). If no type is given, TXT is assumed. When the type is PTR,
the data can be an IP address, written as normal; inversion and the addition of
in-addr.arpa or ip6.arpa happens automatically. For example:
${lookup dnsdb{ptr=192.168.4.5}{$value}fail}
If the data for a PTR record is not a syntactically valid IP address, it is not altered and nothing is added.
For any record type, if multiple records are found (or, for A6 lookups, if a single record leads to multiple addresses), the data is returned as a concatenation, with newline as the default separator. The order, of course, depends on the DNS resolver. You can specify a different separator character between multiple records by putting a right angle-bracket followed immediately by the new separator at the start of the query. For example:
${lookup dnsdb{>: a=host1.example}}
It is permitted to specify a space as the separator character. Further whitespace is ignored.
For SRV records, the priority, weight, port, and host name are returned for each record, separated by spaces.
For MX records, both the preference value and the host name are returned for each record, separated by a space. However, if you want only host names, you can use the pseudo-type MXH:
${lookup dnsdb{mxh=a.b.example}}
In this case, the preference values are omitted.
Another pseudo-type is ZNS (for zone NS). It performs a lookup for NS records on the given domain, but if none are found, it removes the first component of the domain name, and tries again. This process continues until NS records are found or there are no more components left (or there is a DNS error). In other words, it may return the name servers for a top-level domain, but it never returns the root name servers. If there are no NS records for the top-level domain, the lookup fails. Consider these examples:
${lookup dnsdb{zns=xxx.quercite.com}}
${lookup dnsdb{zns=xxx.edu}}
Assuming that in each case there are no NS records for the full domain name, the first returns the name servers for quercite.com, and the second returns the name servers for edu.
You should be careful about how you use this lookup because, unless the top-level domain does not exist, the lookup always returns some host names. The sort of use to which this might be put is for seeing if the name servers for a given domain are on a blacklist. You can probably assume that the name servers for the high-level domains such as com or co.uk are not going to be on such a list.
In the previous section, dnsdb lookups for a single domain are described. However, you can specify a list of domains or IP addresses in a single dnsdb lookup. The list is specified in the normal Exim way, with colon as the default separator, but with the ability to change this. For example:
${lookup dnsdb{one.domain.com:two.domain.com}}
${lookup dnsdb{a=one.host.com:two.host.com}}
${lookup dnsdb{ptr = <; 1.2.3.4 ; 4.5.6.8}}
In order to retain backwards compatibility, there is one special case: if the lookup type is PTR and no change of separator is specified, Exim looks to see if the rest of the string is precisely one IPv6 address. In this case, it does not treat it as a list.
The data from each lookup is concatenated, with newline separators by default, in the same way that multiple DNS records for a single item are handled. A different separator can be specified, as described above.
The dnsdb lookup fails only if all the DNS lookups fail. If there is a temporary DNS error for any of them, the behaviour is controlled by an optional keyword followed by a comma that may appear before the record type. The possible keywords are defer_strict, defer_never, and defer_lax. With strict behaviour, any temporary DNS error causes the whole lookup to defer. With never behaviour, a temporary DNS error is ignored, and the behaviour is as if the DNS lookup failed to find anything. With lax behaviour, all the queries are attempted, but a temporary DNS error causes the whole lookup to defer only if none of the other lookups succeed. The default is lax, so the following lookups are equivalent:
${lookup dnsdb{defer_lax,a=one.host.com:two.host.com}}
${lookup dnsdb{a=one.host.com:two.host.com}}
Thus, in the default case, as long as at least one of the DNS lookups yields some data, the lookup succeeds.
The original LDAP implementation came from the University of Michigan; this has become Open LDAP, and there are now two different releases. Another implementation comes from Netscape, and Solaris 7 and subsequent releases contain inbuilt LDAP support. Unfortunately, though these are all compatible at the lookup function level, their error handling is different. For this reason it is necessary to set a compile-time variable when building Exim with LDAP, to indicate which LDAP library is in use. One of the following should appear in your Local/Makefile:
LDAP_LIB_TYPE=UMICHIGAN LDAP_LIB_TYPE=OPENLDAP1 LDAP_LIB_TYPE=OPENLDAP2 LDAP_LIB_TYPE=NETSCAPE LDAP_LIB_TYPE=SOLARIS
If LDAP_LIB_TYPE is not set, Exim assumes OPENLDAP1, which has the same interface as the University of Michigan version.
There are three LDAP lookup types in Exim. These behave slightly differently in the way they handle the results of a query:
ldap requires the result to contain just one entry; if there are more, it gives an error.
ldapdn also requires the result to contain just one entry, but it is the Distinguished Name that is returned rather than any attribute values.
ldapm permits the result to contain more than one entry; the attributes from all of them are returned.
For ldap and ldapm, if a query finds only entries with no attributes, Exim behaves as if the entry did not exist, and the lookup fails. The format of the data returned by a successful lookup is described in the next section. First we explain how LDAP queries are coded.
An LDAP query takes the form of a URL as defined in RFC 2255. For example, in the configuration of a redirect router one might have this setting:
data = ${lookup ldap \
{ldap:///cn=$local_part,o=University%20of%20Cambridge,\
c=UK?mailbox?base?}}
The URL may begin with ldap or ldaps if your LDAP library supports secure (encrypted) LDAP connections. The second of these ensures that an encrypted TLS connection is used.
Two levels of quoting are required in LDAP queries, the first for LDAP itself and the second because the LDAP query is represented as a URL. Furthermore, within an LDAP query, two different kinds of quoting are required. For this reason, there are two different LDAP-specific quoting operators.
The quote_ldap operator is designed for use on strings that are part of filter specifications. Conceptually, it first does the following conversions on the string:
* => \2A ( => \28 ) => \29 \ => \5C
in accordance with RFC 2254. The resulting string is then quoted according to the rules for URLs, that is, all characters except
! $ ' - . _ ( ) * +
are converted to their hex values, preceded by a percent sign. For example:
${quote_ldap: a(bc)*, a<yz>; }
yields
%20a%5C28bc%5C29%5C2A%2C%20a%3Cyz%3E%3B%20
Removing the URL quoting, this is (with a leading and a trailing space):
a\28bc\29\2A, a<yz>;
The quote_ldap_dn operator is designed for use on strings that are part of base DN specifications in queries. Conceptually, it first converts the string by inserting a backslash in front of any of the following characters:
, + " \ < > ;
It also inserts a backslash before any leading spaces or # characters, and before any trailing spaces. (These rules are in RFC 2253.) The resulting string is then quoted according to the rules for URLs. For example:
${quote_ldap_dn: a(bc)*, a<yz>; }
yields
%5C%20a(bc)*%5C%2C%20a%5C%3Cyz%5C%3E%5C%3B%5C%20
Removing the URL quoting, this is (with a trailing space):
\ a(bc)*\, a\<yz\>\;\
There are some further comments about quoting in the section on LDAP authentication below.
The connection to an LDAP server may either be over TCP/IP, or, when OpenLDAP is in use, via a Unix domain socket. The example given above does not specify an LDAP server. A server that is reached by TCP/IP can be specified in a query by starting it with
ldap://<hostname>:<port>/...
If the port (and preceding colon) are omitted, the standard LDAP port (389) is used. When no server is specified in a query, a list of default servers is taken from the ldap_default_servers configuration option. This supplies a colon-separated list of servers which are tried in turn until one successfully handles a query, or there is a serious error. Successful handling either returns the requested data, or indicates that it does not exist. Serious errors are syntactical, or multiple values when only a single value is expected. Errors which cause the next server to be tried are conne