From 0d01bcc2f9bf16e3f384549f395c1a173b8a4c18 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 28 Mar 2017 11:00:05 +0200 Subject: Rewrite ACLs --- conf.d/010_main.conf | 6 +- conf.d/020_acl.conf | 335 ++++++++++++++++++++++++--------------------------- exim.conf | 3 + list.d/aliases.map | 0 list.d/users.list | 0 5 files changed, 164 insertions(+), 180 deletions(-) create mode 100644 list.d/aliases.map create mode 100644 list.d/users.list diff --git a/conf.d/010_main.conf b/conf.d/010_main.conf index 08694c8..e693144 100644 --- a/conf.d/010_main.conf +++ b/conf.d/010_main.conf @@ -68,8 +68,10 @@ hostlist relay_from_hosts = localhost # manual for details. The lists above are used in the access control lists for # checking incoming messages. The names of these ACLs are defined here: -acl_smtp_rcpt = acl_check_rcpt -acl_smtp_data = acl_check_data +acl_smtp_rcpt = ${if ={587}{$interface_port} {acl_mua_rcpt} {acl_mta_rcpt} +acl_smtp_mail = ${if ={587}{$interface_port} {acl_mua_mail} {accept} +acl_smtp_data = ${if ={587}{$interface_port} {acl_mua_data} {acl_mta_data} +acl_smtp_dkim = acl_check_dkim # You should not change those settings until you understand how ACLs work. diff --git a/conf.d/020_acl.conf b/conf.d/020_acl.conf index 61dc33f..a909910 100644 --- a/conf.d/020_acl.conf +++ b/conf.d/020_acl.conf @@ -5,190 +5,169 @@ begin acl -# This access control list is used for every RCPT command in an incoming -# SMTP message. The tests are run in order until the address is either -# accepted or denied. - -acl_check_rcpt: - - # Accept if the source is local SMTP (i.e. not over TCP/IP). We do this by - # testing for an empty sending host field. - - accept hosts = : - control = dkim_disable_verify - - ############################################################################# - # The following section of the ACL is concerned with local parts that contain - # @ or % or ! or / or | or dots in unusual places. - # - # The characters other than dots are rarely found in genuine local parts, but - # are often tried by people looking to circumvent relaying restrictions. - # Therefore, although they are valid in local parts, these rules lock them - # out, as a precaution. - # - # Empty components (two dots in a row) are not valid in RFC 2822, but Exim - # allows them because they have been encountered. (Consider local parts - # constructed as "firstinitial.secondinitial.familyname" when applied to - # someone like me, who has no second initial.) However, a local part starting - # with a dot or containing /../ can cause trouble if it is used as part of a - # file name (e.g. for a mailing list). This is also true for local parts that - # contain slashes. A pipe symbol can also be troublesome if the local part is - # incorporated unthinkingly into a shell command line. - # - # Two different rules are used. The first one is stricter, and is applied to - # messages that are addressed to one of the local domains handled by this - # host. The line "domains = +local_domains" restricts it to domains that are - # defined by the "domainlist local_domains" setting above. The rule blocks - # local parts that begin with a dot or contain @ % ! / or |. If you have - # local accounts that include these characters, you will have to modify this - # rule. - - deny message = Restricted characters in address - domains = +local_domains - local_parts = ^[.] : ^.*[@%!/|] - - # The second rule applies to all other domains, and is less strict. The line - # "domains = !+local_domains" restricts it to domains that are NOT defined by - # the "domainlist local_domains" setting above. The exclamation mark is a - # negating operator. This rule allows your own users to send outgoing - # messages to sites that use slashes and vertical bars in their local parts. - # It blocks local parts that begin with a dot, slash, or vertical bar, but - # allows these characters within the local part. However, the sequence /../ - # is barred. The use of @ % and ! is blocked, as before. The motivation here - # is to prevent your users (or your users' viruses) from mounting certain - # kinds of attack on remote sites. - - deny message = Restricted characters in address - domains = !+local_domains - local_parts = ^[./|] : ^.*[@%!] : ^.*/\\.\\./ - ############################################################################# - - # Accept mail to postmaster in any local domain, regardless of the source, - # and without verifying the sender. - - accept local_parts = postmaster - domains = +local_domains - - # Deny unless the sender address can be verified. - - require verify = sender - - # Accept if the message comes from one of the hosts for which we are an - # outgoing relay. It is assumed that such hosts are most likely to be MUAs, - # so we set control=submission to make Exim treat the message as a - # submission. It will fix up various errors in the message, for example, the - # lack of a Date: header line. If you are actually relaying out out from - # MTAs, you may want to disable this. If you are handling both relaying from - # MTAs and submissions from MUAs you should probably split them into two - # lists, and handle them differently. - - # Recipient verification is omitted here, because in many cases the clients - # are dumb MUAs that don't cope well with SMTP error responses. If you are - # actually relaying out from MTAs, you should probably add recipient - # verification here. - - # Note that, by putting this test before any DNS black list checks, you will - # always accept from these hosts, even if they end up on a black list. The - # assumption is that they are your friends, and if they get onto a black - # list, it is a mistake. - - accept hosts = +relay_from_hosts - control = submission - control = dkim_disable_verify - - # Accept if the message arrived over an authenticated connection, from - # any host. Again, these messages are usually from MUAs, so recipient - # verification is omitted, and submission mode is set. And again, we do this - # check before any black list tests. - - accept authenticated = * - control = submission - control = dkim_disable_verify + +###################### +# GENERAL CHECK ACLs # +###################### + +# Ensures that the remote host has introduced itself. +acl_check_rcpt_introduction: # Insist that a HELO/EHLO was accepted. + require condition = ${if def:sender_helo_name} + set acl_m_msg = HELO/EHLO command required. + + accept + + +# Syntactic validation of fields. +acl_check_rcpt_syntax: + + # Deny if the local parts is malformed. + deny local_parts = ^[.] : ^.*[@%!/|] + set acl_m_msg = Invalid local part. + + accept + + +# Checks the link between a given mail alias and a given user. +# $acl_arg1: alias to check +# $acl_arg2: user +acl_check_alias: + + # Accept if the alias belongs to the user. + accept condition = ${lookup{$acl_arg1}lsearch{ALIASMAP} \ + {${if eq{$value}{$acl_arg2} {yes}{no}}} {no}} + + # Deny otherwise + deny + + +# Checks if the host is allowed to send messages according to the local whitelist, DNS blocklists and SPF policy. +acl_check_rcpt_host_policy: + + # Accept all messages from trusted hosts and relays. + accept hosts = +redirected_from_hosts + + # Deny messages from hosts known to be bad. + deny dnslists = sbl-xbl.spamhaus.org : bl.spamcop.net + set acl_m_msg = Rejected: $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text + + # Accept if the host is an authorized sender according to the SPF policy for the domain (SPF pass). + accept set acl_m_spf = ${run{/usr/bin/spfquery.mail-spf-perl \ + --ip ${quote:$sender_host_address} \ + --identity ${if def:sender_address_domain \ + {--scope mfrom --identity ${quote:$sender_address}} \ + {--scope helo --identity ${quote:$sender_helo_name}}}}} + condition = ${if eq {$acl_m_spf}{0}{yes}{no}} + + # Deny if the host is explicitely not an authorized sender according to the SPF policy for the domain (SPF fail). + deny condition = ${if eq {$acl_m_spf}{1}{yes}{no}} + set acl_m_msg = [SPF] $sender_host_address is not allowed to send mail from \ + ${if def:sender_address_domain {$sender_address_domain}{$sender_helo_name}}. \ + Please see http://www.openspf.org/Why?scope=${if def:sender_address_domain \ + {mfrom}{helo}};identity=${if def:sender_address_domain \ + {$sender_address}{$sender_helo_name}};ip=$sender_host_address + + # Deny messages from hosts listed as non-MTA in the PBL, for which SPF couldn't determine a policy. + deny dnslists = pbl.spamhaus.org + set acl_m_msg = Rejected: $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text + + # Accept otherwise + accept + + +######################### +# MUA ACLs (submission) # +######################### + +acl_mua_rcpt: + + require encrypted = * + message = Encryption required. + + require acl = acl_check_rcpt_introduction + message = Courtesy protocol violation: $acl_m_msg + + require authenticated = * + control = submission + control = dkim_disable_verify + message = Authentication required. + + require acl = acl_check_rcpt_syntax + message = Syntactic validation failed: $acl_m_msg - require message = nice hosts say HELO first - condition = ${if def:sender_helo_name} - - # Insist that any other recipient address that we accept is either in one of - # our local domains, or is in a domain for which we explicitly allow - # relaying. Any other domain is rejected as being unacceptable for relaying. - - require message = relay not permitted - domains = +local_domains : +relay_to_domains - - # We also require all accepted addresses to be verifiable. This check will - # do local part verification for local domains, but only check the domain - # for remote domains. The only way to check local parts for the remote - # relay domains is to use a callout (add /callout), but please read the - # documentation about callouts before doing this. - - require verify = recipient - - ############################################################################# - # There are no default checks on DNS black lists because the domains that - # contain these lists are changing all the time. However, here are two - # examples of how you can get Exim to perform a DNS black list lookup at this - # point. The first one denies, whereas the second just warns. - # - # deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text - # dnslists = black.list.example - # - # warn dnslists = black.list.example - # add_header = X-Warning: $sender_host_address is in a black list at $dnslist_domain - # log_message = found in $dnslist_domain - ############################################################################# - - ############################################################################# - # This check is commented out because it is recognized that not every - # sysadmin will want to do it. If you enable it, the check performs - # Client SMTP Authorization (csa) checks on the sending host. These checks - # do DNS lookups for SRV records. The CSA proposal is currently (May 2005) - # an Internet draft. You can, of course, add additional conditions to this - # ACL statement to restrict the CSA checks to certain hosts only. - # - # require verify = csa - ############################################################################# - - # At this point, the address has passed all the checks that have been - # configured, so we accept it unconditionally. + require verify = sender + message = Sender verification failed. accept -# This ACL is used after the contents of a message have been received. This -# is the ACL in which you can test a message's headers or body, and in -# particular, this is where you can invoke external virus or spam scanners. -# Some suggested ways of configuring these tests are shown below, commented -# out. Without any tests, this ACL accepts all messages. If you want to use -# such tests, you must ensure that Exim is compiled with the content-scanning -# extension (WITH_CONTENT_SCAN=yes in Local/Makefile). - -acl_check_data: - - # Deny if the message contains an overlong line. Per the standards - # we should never receive one such via SMTP. - # - deny message = maximum allowed line length is 998 octets, \ - got $max_received_linelength - condition = ${if > {$max_received_linelength}{998}} - - # Deny if the message contains a virus. Before enabling this check, you - # must install a virus scanner and set the av_scanner option above. - # - # deny malware = * - # message = This message contains a virus ($malware_name). - - # Add headers to a message if it is judged to be spam. Before enabling this, - # you must install SpamAssassin. You may also need to set the spamd_address - # option above. - # - # warn spam = nobody - # add_header = X-Spam_score: $spam_score\n\ - # X-Spam_score_int: $spam_score_int\n\ - # X-Spam_bar: $spam_bar\n\ - # X-Spam_report: $spam_report - - # Accept the message. +acl_mua_mail: + + require acl = acl_check_alias $sender_address $authenticated_id + message = Envelope address mismatch: $authenticated_id is not authorized to use $sender_address. + + accept + + +acl_mua_data: + + require acl = acl_check_alias ${address:$h_from:} $authenticated_id + message = Header address mismatch: $authenticated_id is not authorized to use ${address:$h_from:}. + + accept + + +############ +# MTA ACLs # +############ + +acl_mta_rcpt: + + require acl = acl_check_rcpt_introduction + message = Courtesy protocol violation: $acl_m_msg + + require acl = acl_check_rcpt_host_policy + message = $acl_m_msg + + require acl = acl_check_rcpt_syntax + message = Syntactic validation failed: $acl_m_msg + + require verify = sender + message = Sender verification failed. + + require domains = +local_domains : +relay_to_domains + message = Unhandled destination. + + require verify = recipient + message = Recipient verification failed. + + accept + + +acl_mta_data: + + # Deny if the message contains an overlong line. Per the standards we should never receive one such via SMTP. + deny condition = ${if > {$max_received_linelength}{998}} + set acl_m_msg = Maximum allowed line length is 998 octets, got $max_received_linelength. + + accept + + +# Checks the validity of each signatures in the message, and the presence of a required signature for the sender domain. +# Global var to enable mandatory signature check: dkim_verify_signers = $sender_address_domain:$dkim_signers +acl_mta_dkim: + + # Deny messages with invalid signatures. + deny dkim_status = fail + message = [DKIM] invalid signature ($dkim_verify_reason). + + # Deny messages with no signature for domains which are explicitely requiring one. + deny dkim_status = none + condition = ${if match \ + {${run{/usr/bin/dig +short TXT ${quote:_domainkey.$sender_host_address}}}}{/o=-/} \ + {yes}{no}} + message = [DKIM] required signature is missing. accept diff --git a/exim.conf b/exim.conf index a6819f7..b61efd1 100644 --- a/exim.conf +++ b/exim.conf @@ -38,6 +38,9 @@ CONFDIR = /etc/exim +USERLIST = CONFDIR/list.d/users.list +ALIASMAP = CONFDIR/list.d/aliases.map + .include CONFDIR/conf.d/010_main.conf .include CONFDIR/conf.d/020_acl.conf .include CONFDIR/conf.d/030_routers.conf diff --git a/list.d/aliases.map b/list.d/aliases.map new file mode 100644 index 0000000..e69de29 diff --git a/list.d/users.list b/list.d/users.list new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3