From aa3062fa1afe60c42d7ecd842f03169006a9d84b Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 2 Jul 2020 12:03:39 +0100 Subject: [PATCH] Fix greylisting for Exim 4.94 --- exim-4.94-config.patch | 68 +++++++++++++++++++++--------------- exim-greylist.conf.inc | 79 +++++++++++++++++++++++++++++++++--------- 2 files changed, 104 insertions(+), 43 deletions(-) diff --git a/exim-4.94-config.patch b/exim-4.94-config.patch index f4fd003..43e951b 100644 --- a/exim-4.94-config.patch +++ b/exim-4.94-config.patch @@ -395,7 +395,20 @@ index 3423ee0..7d1e552 100644 # For spam scanning, there is a similar option that defines the interface to -@@ -157,7 +159,7 @@ acl_smtp_data = acl_check_data +@@ -147,6 +149,12 @@ acl_smtp_data = acl_check_data + # spamd_address = 127.0.0.1 783 + + ++# Set the default sqlite database file for greylisting. Uncomment this ++# if you use the greylisting ACLs defined below. ++ ++# sqlite_dbfile = /var/spool/exim/db/greylist.db ++ ++ + # If Exim is compiled with support for TLS, you may want to enable the + # following options so that Exim allows clients to make encrypted + # connections. In the authenticators section below, there are template +@@ -157,7 +165,7 @@ acl_smtp_data = acl_check_data # Allow any client to use TLS. @@ -404,7 +417,7 @@ index 3423ee0..7d1e552 100644 # Specify the location of the Exim server's TLS certificate and private key. # The private key must not be encrypted (password protected). You can put -@@ -165,8 +167,8 @@ acl_smtp_data = acl_check_data +@@ -165,8 +173,8 @@ acl_smtp_data = acl_check_data # need the first setting, or in separate files, in which case you need both # options. @@ -415,7 +428,7 @@ index 3423ee0..7d1e552 100644 # For OpenSSL, prefer EC- over RSA-authenticated ciphers # tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAULT -@@ -180,8 +182,8 @@ acl_smtp_data = acl_check_data +@@ -180,8 +188,8 @@ acl_smtp_data = acl_check_data # them you should also allow TLS-on-connect on the traditional but # non-standard port 465. @@ -426,7 +439,7 @@ index 3423ee0..7d1e552 100644 # Specify the domain you want to be added to all unqualified addresses -@@ -239,6 +241,24 @@ never_users = root +@@ -239,6 +247,24 @@ never_users = root host_lookup = * @@ -451,7 +464,7 @@ index 3423ee0..7d1e552 100644 # The setting below causes Exim to try to initialize the system resolver # library with DNSSEC support. It has no effect if your library lacks -@@ -369,8 +389,8 @@ timeout_frozen_after = 7d +@@ -369,8 +395,8 @@ timeout_frozen_after = 7d # Note that TZ is handled separately by the timezone runtime option # and TIMEZONE_DEFAULT buildtime option. @@ -462,7 +475,7 @@ index 3423ee0..7d1e552 100644 -@@ -381,6 +401,29 @@ timeout_frozen_after = 7d +@@ -381,6 +407,29 @@ timeout_frozen_after = 7d begin acl @@ -492,7 +505,7 @@ index 3423ee0..7d1e552 100644 # 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. -@@ -392,6 +435,7 @@ acl_check_rcpt: +@@ -392,6 +441,7 @@ acl_check_rcpt: accept hosts = : control = dkim_disable_verify @@ -500,7 +513,7 @@ index 3423ee0..7d1e552 100644 ############################################################################# # The following section of the ACL is concerned with local parts that contain -@@ -445,7 +489,8 @@ acl_check_rcpt: +@@ -445,7 +495,8 @@ acl_check_rcpt: accept local_parts = postmaster domains = +local_domains @@ -510,7 +523,7 @@ index 3423ee0..7d1e552 100644 require verify = sender -@@ -471,6 +516,7 @@ acl_check_rcpt: +@@ -471,6 +522,7 @@ acl_check_rcpt: accept hosts = +relay_from_hosts control = submission control = dkim_disable_verify @@ -518,7 +531,7 @@ index 3423ee0..7d1e552 100644 # Accept if the message arrived over an authenticated connection, from # any host. Again, these messages are usually from MUAs, so recipient -@@ -480,6 +526,7 @@ acl_check_rcpt: +@@ -480,6 +532,7 @@ acl_check_rcpt: accept authenticated = * control = submission control = dkim_disable_verify @@ -526,7 +539,7 @@ index 3423ee0..7d1e552 100644 # Insist that a HELO/EHLO was accepted. -@@ -505,7 +552,8 @@ acl_check_rcpt: +@@ -505,7 +558,8 @@ acl_check_rcpt: # 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 @@ -536,7 +549,7 @@ index 3423ee0..7d1e552 100644 # # deny dnslists = black.list.example # message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text -@@ -513,6 +561,10 @@ acl_check_rcpt: +@@ -513,6 +567,10 @@ acl_check_rcpt: # 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 @@ -547,7 +560,7 @@ index 3423ee0..7d1e552 100644 ############################################################################# ############################################################################# -@@ -539,6 +591,10 @@ acl_check_rcpt: +@@ -539,6 +597,10 @@ acl_check_rcpt: # set acl_m_content_filter = ${lookup PER_RCPT_CONTENT_FILTER} ############################################################################# @@ -558,7 +571,7 @@ index 3423ee0..7d1e552 100644 # At this point, the address has passed all the checks that have been # configured, so we accept it unconditionally. -@@ -588,21 +644,32 @@ acl_check_data: +@@ -588,21 +650,32 @@ acl_check_data: message = header syntax log_message = header syntax ($acl_verify_message) @@ -587,19 +600,20 @@ index 3423ee0..7d1e552 100644 - # 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. -+ # Bypass SpamAssassin checks if the message is too large. - # +- # - # 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 ++ # Bypass SpamAssassin checks if the message is too large. ++ # + # accept condition = ${if >={$message_size}{100000} {1}} + # add_header = X-Spam-Note: SpamAssassin run bypassed due to message size ############################################################################# # No more tests if PRDR was actively used. -@@ -616,11 +683,63 @@ acl_check_data: +@@ -616,11 +689,63 @@ acl_check_data: # condition = ... ############################################################################# @@ -664,7 +678,7 @@ index 3423ee0..7d1e552 100644 ###################################################################### -@@ -722,7 +841,7 @@ system_aliases: +@@ -722,7 +847,7 @@ system_aliases: driver = redirect allow_fail allow_defer @@ -673,7 +687,7 @@ index 3423ee0..7d1e552 100644 # user = exim file_transport = address_file pipe_transport = address_pipe -@@ -760,7 +879,7 @@ userforward: +@@ -760,7 +885,7 @@ userforward: # local_part_suffix = +* : -* # local_part_suffix_optional file = $home/.forward @@ -682,7 +696,7 @@ index 3423ee0..7d1e552 100644 no_verify no_expn check_ancestor -@@ -768,6 +887,12 @@ userforward: +@@ -768,6 +893,12 @@ userforward: pipe_transport = address_pipe reply_transport = address_reply @@ -695,7 +709,7 @@ index 3423ee0..7d1e552 100644 # This router matches local user mailboxes. If the router fails, the error # message is "Unknown user". -@@ -809,6 +934,25 @@ remote_smtp: +@@ -809,6 +940,25 @@ remote_smtp: driver = smtp message_size_limit = ${if > {$max_received_linelength}{998} {1}{0}} @@ -721,7 +735,7 @@ index 3423ee0..7d1e552 100644 # This transport is used for delivering messages to a smarthost, if the # smarthost router is enabled. This starts from the same basis as -@@ -861,8 +1005,8 @@ local_delivery: +@@ -861,8 +1011,8 @@ local_delivery: delivery_date_add envelope_to_add return_path_add @@ -732,7 +746,7 @@ index 3423ee0..7d1e552 100644 # This transport is used for handling pipe deliveries generated by alias or -@@ -895,6 +1039,16 @@ address_reply: +@@ -895,6 +1045,16 @@ address_reply: driver = autoreply @@ -749,7 +763,7 @@ index 3423ee0..7d1e552 100644 ###################################################################### # RETRY CONFIGURATION # -@@ -935,6 +1089,21 @@ begin rewrite +@@ -935,6 +1095,21 @@ begin rewrite # AUTHENTICATION CONFIGURATION # ###################################################################### @@ -771,7 +785,7 @@ index 3423ee0..7d1e552 100644 # The following authenticators support plaintext username/password # authentication using the standard PLAIN mechanism and the traditional # but non-standard LOGIN mechanism, with Exim acting as the server. -@@ -950,7 +1119,7 @@ begin rewrite +@@ -950,7 +1125,7 @@ begin rewrite # The default RCPT ACL checks for successful authentication, and will accept # messages from authenticated users from anywhere on the Internet. @@ -780,7 +794,7 @@ index 3423ee0..7d1e552 100644 # PLAIN authentication has no server prompts. The client sends its # credentials in one lump, containing an authorization ID (which we do not -@@ -964,7 +1133,7 @@ begin authenticators +@@ -964,7 +1139,7 @@ begin authenticators # driver = plaintext # server_set_id = $auth2 # server_prompts = : @@ -789,7 +803,7 @@ index 3423ee0..7d1e552 100644 # server_advertise_condition = ${if def:tls_in_cipher } # LOGIN authentication has traditional prompts and responses. There is no -@@ -976,7 +1145,7 @@ begin authenticators +@@ -976,7 +1151,7 @@ begin authenticators # driver = plaintext # server_set_id = $auth1 # server_prompts = <| Username: | Password: diff --git a/exim-greylist.conf.inc b/exim-greylist.conf.inc index bb22907..b73ea5e 100644 --- a/exim-greylist.conf.inc +++ b/exim-greylist.conf.inc @@ -1,11 +1,44 @@ -# $Id: acl-greylist-sqlite,v 1.3 2007/11/25 19:17:28 dwmw2 Exp $ +# +# Exim ACL for greylisting. David Woodhouse +# +# For full background on the logic behind greylisting and how this +# ACL works, see https://github.com/Exim/exim/wiki/SimpleGreylisting +# -GREYDB=/var/spool/exim/db/greylist.db +# UPDATING TO EXIM 4.94+ +# ====================== +# +# Previous versions of this ACL specified the sqlite database filename +# in the sqlite lookup strings directly, but since Exim 4.94 is it no +# longer permitted to mix "tainted" text which comes from the message +# itself, with the filename. Thus, you now have to set +# +# sqlite_dbfile = /var/spool/exim/db/greylist.db +# +# ... in the main configuration because it can't be specified within +# the ACL in this file any more. -# ACL for greylisting. Place reason(s) for greylisting into a variable named -# $acl_m_greylistreasons before invoking with 'require acl = greylist_mail'. -# The reasons should be separate lines of text, and will be reported in -# the SMTP rejection message as well as the log message. +# USING THIS ACL +# ============== +# +# First set sqlite_dbfile in the main configuration file to point to +# the greylist sqlite database, as described above. +# +# In your main ACLs, gather reason(s) for greylisting into a variable +# named $acl_m_greylistreasons before invoking this ACL with +# 'require acl = greylist_mail'. The reasons should be separate lines +# of text, and will be reported in the SMTP rejection message as well +# as the log message. Anything "suspicious" about the email can be +# used as criteria here — being HTML, having even a few SpamAssassin +# points, even lacking SPF authorisation (which is OK for greylisting +# although you should never reject outright for an SPF "failure" +# because of the flaws in SPF). +# +# Obviously you need to .include this file too in order to be able +# to invoke this greylist_mail ACL. + +# HOW IT WORKS +# ============ # # When a suspicious mail is seen, we temporarily reject it and wait to see # if the sender tries again. Most spam robots won't bother. Real mail hosts @@ -44,15 +77,13 @@ GREYDB=/var/spool/exim/db/greylist.db # greylist_mail: - # First, accept if it there's absolutely nothing suspicious about it... - accept condition = ${if eq{$acl_m_greylistreasons}{} {1}} - # ... or if it was generated locally or by authenticated clients. + # Firstly, accept if it was generated locally or by authenticated clients. accept hosts = : accept authenticated = * # Secondly, there's _absolutely_ no point in greylisting mail from # hosts which are known to resend their mail. Just accept it. - accept condition = ${lookup sqlite {GREYDB SELECT host from resenders \ + accept condition = ${lookup sqlite {SELECT host from resenders \ WHERE helo='${quote_sqlite:$sender_helo_name}' \ AND host='$sender_host_address';} {1}} @@ -62,15 +93,28 @@ greylist_mail: # Attempt to look up this mail in the greylist database. If it's there, # remember the expiry time for it; we need to make sure they've waited # long enough. - warn set acl_m_greyexpiry = ${lookup sqlite {GREYDB SELECT expire FROM greylist \ + warn set acl_m_greyexpiry = ${lookup sqlite {SELECT expire FROM greylist \ WHERE id='${quote_sqlite:$acl_m_greyident}';}{$value}} + + # If there's absolutely nothing suspicious about the email, accept it. BUT... + accept condition = ${if eq {$acl_m_greylistreasons}{} {1}} + condition = ${if eq {$acl_m_greyexpiry}{} {1}} + + # ..if this same mail was greylisted before (perhaps because it came from a + # host which *was* suspicious), then we still want to mark that original host + # as a "known resender". If we don't, then hosts which attempt to deliver from + # a dodgy Legacy IP address but then fall back to using IPv6 after greylisting + # will *never* see their Legacy IP address added to the 'known resenders' list. + accept condition = ${if eq {$acl_m_greylistreasons}{} {1}} + acl = write_known_resenders + # If the mail isn't already the database -- i.e. if the $acl_m_greyexpiry # variable we just looked up is empty -- then try to add it now. This is # where the 5 minute timeout is set ($tod_epoch + 300), should you wish # to change it. warn condition = ${if eq {$acl_m_greyexpiry}{} {1}} - set acl_m_dontcare = ${lookup sqlite {GREYDB INSERT INTO greylist \ + set acl_m_dontcare = ${lookup sqlite {INSERT INTO greylist \ VALUES ( '$acl_m_greyident', \ '${eval10:$tod_epoch+300}', \ '$sender_host_address', \ @@ -79,7 +123,7 @@ greylist_mail: # Be paranoid, and check if the insertion succeeded (by doing another lookup). # Otherwise, if there's a database error we might end up deferring for ever. defer condition = ${if eq {$acl_m_greyexpiry}{} {1}} - condition = ${lookup sqlite {GREYDB SELECT expire FROM greylist \ + condition = ${lookup sqlite {SELECT expire FROM greylist \ WHERE id='${quote_sqlite:$acl_m_greyident}';} {1}} message = Your mail was considered suspicious for the following reason(s):\n$acl_m_greylistreasons \ The mail has been greylisted for 5 minutes, after which it should be accepted. \ @@ -105,13 +149,16 @@ greylist_mail: You should wait another ${eval10:$acl_m_greyexpiry-$tod_epoch} seconds.\n\ Reason(s) for greylisting: \n$acl_m_greylistreasons + accept acl = write_known_resenders + +write_known_resenders: # The message was listed but it's been more than five minutes. Accept it now and whitelist # the _original_ sending host by its { IP, HELO } so that we don't delay its mail again. - warn set acl_m_orighost = ${lookup sqlite {GREYDB SELECT host FROM greylist \ + warn set acl_m_orighost = ${lookup sqlite {SELECT host FROM greylist \ WHERE id='${quote_sqlite:$acl_m_greyident}';}{$value}} - set acl_m_orighelo = ${lookup sqlite {GREYDB SELECT helo FROM greylist \ + set acl_m_orighelo = ${lookup sqlite {SELECT helo FROM greylist \ WHERE id='${quote_sqlite:$acl_m_greyident}';}{$value}} - set acl_m_dontcare = ${lookup sqlite {GREYDB INSERT INTO resenders \ + set acl_m_dontcare = ${lookup sqlite {INSERT INTO resenders \ VALUES ( '$acl_m_orighost', \ '${quote_sqlite:$acl_m_orighelo}', \ '$tod_epoch' ); }}