[Exim] May I cause SMTP delay for spammers (a.k.a. teergrubi…

Startseite
Nachricht löschen
Nachricht beantworten
Autor: Tor Slettnes
Datum:  
To: exim-users
Betreff: [Exim] May I cause SMTP delay for spammers (a.k.a. teergrubing)?
Hi all,

I previously ran SpamAssassin via the SA-Exim local_scan plugin
(http://marc.merlins.org/linux/exim/sa.html), partly due to its
"teergrubing" facility. The way this worked was that if a message was
deemed to be spam, then my server would stall the sending host by
sending a multi-line 451 response after the DATA, one line about every
10 seconds for 30 minutes or so -- essentially occupying one out of
~65500 sockets available on the spamming machine for that long as well.

Over time, I found that it was only so effective, because most spamware
clients do not honor the "-" between the status code (in this case 451)
and the following text. They would simply disconnect after the first
line of the response.

So I now do something a little different. For various reasons I no
longer use SA-Exim; instead, I have ACLs with "delay = 20s" at various
points in the SMTP transaction (see below). For instance,

  - If a host provides a suspicious HELO greeting, they are stalled for
    20 seconds right there, again after the MAIL, RCPT, and DATA
    commands, after which the message is rejected.


  - If the sender/callout verfication fails, that's 20 seconds after
    MAIL, another 20 seconds after RCPT, and a third time after DATA
    (but in this case, I do eventually accept the mail, unless SA flags
    it as spam as well).


  - If the recipient address does not exist, then that's 20 seconds
    after RCPT TO:.


- And so on, and so forth. I'll include the various ACLs below.


A typical dialogue might be:

        <-  220 dot.slett.net ESMTP Exim 4.30 Fri, 19 Mar 2004 23:24:24 -0800
         -> EHLO bogushost


        === 20 second delay ===


        <-  250-net.slett.net Hello plain.knkco.com [216.127.72.3]
        <-  250-SIZE 52428800
        <-  250-8BITMIME
        <-  250-PIPELINING
        <-  250-STARTTLS
        <-  250 HELP
         -> MAIL FROM:<test@???>


        === 20 second delay ===


        <-  250 OK
         -> RCPT TO:<tor@???>


        === 20 second delay ===


        <-  250 Accepted
         -> DATA
        <-  354 Enter message, ending with "." on a line by itself
         -> [...]
        <-  250 OK id=1B4arF-0003lQ-Ak
         -> QUIT


        === 20 second delay ===


        <-  221 net.slett.net closing connection



This has worked great so far (a few days). However, I can see a couple
of issues looming:

   - What if someone else does a "verify = sender/callback=15s" on our
     mail, but they have a misconfigured mailname on their system?
     They would be subject to a 20 second wait after HELO, and thus
     time out.
     (I don't expect this to be a major concern in practice -- the
     default callback timeout is 30s; and if my mail is ever rejected
     based on a callback, I can always deal with it at that point).


   - Am I breaking any standards?  (RFC2822, etc)
     (Yeah, I know, I'll RTFM one of these days).


   - My machine is not busy (perhaps the largest source of incoming mail
     is this very list), so I don't expect to be processing and stalling
     a whole slew of incoming SMTP connections at once - but is there a
     limit to how many fork()s the Exim daemon creates for processing
     incoming mails?  Is that configurable?


Thanks for any replies!

-tor



My releveant ACL snippets follow:
========================================================================

acl_check_helo:
  # If the remote host greets with an IP address, then stall for 20
  # seconds, and prepare a message in $acl_c0.  Later we will use the
  # presence of this message to stall the sender further and eventually
  # to reject the message after the DATA block.
  accept
       condition   = ${if isip {$sender_helo_name}{true}{false}}
       delay       = 20s
       set acl_c0  = You greeted me with an IP address. \
             What I wanted was your name.



  # Likewise if the peer greets with a name that resolves to our own address
  accept
       condition   = ${if eq {${lookup dnsdb{a=$sender_helo_name} {$value}}} \
                             {$interface_address} \
                             {true}{false}}
       delay       = 20s
       set acl_c0  = Did you say your name was $sender_helo_name? \
             Give me a break, impostor!



  # If HELO verification fails, we stall for 20 seconds, and prepare a
  # message in acl_c1.  We will later use the presence of this message
  # to stall the sender further and generate a warning.
  warn !hosts      = +relay_from_hosts
       !verify     = helo
       set acl_c1  = Remote host $sender_host_address \
                     incorrectly identified itself as $sender_helo_name
       log_message = $acl_c1
       delay       = 20s


accept



acl_check_helo:
  # If the remote host greets with an IP address, then stall for 20
  # seconds, and prepare a message in $acl_c0.  Later we will use the
  # presence of this message to stall the sender further and eventually
  # to reject the message after the DATA block.
  accept
       condition   = ${if isip {$sender_helo_name}{true}{false}}
       delay       = 20s
       set acl_c0  = You greeted me with an IP address. \
             What I wanted was your name.



  # Likewise if the peer greets with a name that resolves to our own address
  accept
       condition   = ${if eq {${lookup dnsdb{a=$sender_helo_name} {$value}}} \
                             {$interface_address} \
                             {true}{false}}
       delay       = 20s
       set acl_c0  = Did you say your name was $sender_helo_name? \
             Give me a break, impostor!



  # If HELO verification fails, we stall for 20 seconds, and prepare a
  # message in acl_c1.  We will later use the presence of this message
  # to stall the sender further and generate a warning.
  warn !hosts      = +relay_from_hosts
       !verify     = helo
       set acl_c1  = Remote host $sender_host_address \
                     incorrectly identified itself as $sender_helo_name
       log_message = $acl_c1
       delay       = 20s


accept



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 = :

# Deny if the local part contains @ or % or / or | or !. These are rarely
# found in genuine local parts, but are often tried by people looking to
# circumvent relaying restrictions.
#
# Also deny if the local part starts with a dot. Empty components aren't
# strictly legal in RFC 2822, but Exim allows them because this is common.
# However, actually starting with a dot may cause trouble if the local part
# is used as a file name (e.g. for a mailing list).
#
deny local_parts = ^.*[@%!/|] : ^\\.


  # Accept mail to postmaster in any local domain.  However, if we
  # previously generated a message in $acl_c0 or $acl_c1, stall the sender.
  #
  accept
       local_parts = postmaster
       domains     = +local_domains : +relay_to_domains
       delay       = ${if or {{def:acl_c0}{def:acl_c1}}{20s}{0s}}



  # If the address is in a local domain or in a domain for which are
  # relaying, but is invalid, stall the sender and then reject.
  #
  deny domains     = +local_domains : +relay_to_domains
       message     = unknown user
       !verify     = recipient/callout=20s,defer_ok,random
       delay       = 20s



  # Otherwise, if the address is in one of our domains, accept it.
  # However, if we have previously generated a message in acl_c0 or acl_c1,
  # stall the sender.
  accept
       domains     = +local_domains : +relay_to_domains
       delay       = ${if or {{def:acl_c0}{def:acl_c1}}{20s}{0s}}




  # Accept if the message comes from one of the hosts for which we are an
  # outgoing relay. 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.
  #
  accept
       hosts       = +relay_from_hosts



  # Accept if the message arrived over an authenticated connection, from
  # any host. Again, these messages are usually from MUAs, so recipient
  # verification is omitted.
  #
  accept
       authenticated = *



  # Reaching the end of the ACL causes a "deny", but we might as well give
  # an explicit message.
  #
  deny message     = relay not permitted
         delay     = 20s





acl_check_data:
   # Deny if we have previously given a reason for doing so in $acl_c0
   deny message     = $acl_c0
        condition   = ${if def:acl_c0 {true}{false}}
        delay       = 20s



   # Add Message-ID if missing
   warn condition   = ${if !def:h_Message-ID: {1}}
        hosts       = +relay_from_hosts
        message     = Message-ID: <E$message_id@$primary_hostname>




   # Deny unless the address list headers are syntactically correct.
   deny message     = Message headers fail syntax check
        !acl        = acl_whitelist_local_deny
        !verify     = header_syntax
        delay       = 20s



   # require that there is a verifiable sender address in at least
   # one of the "Sender:", "Reply-To:", or "From:" header lines.
   warn
        !acl        = acl_whitelist_local_deny
        !verify     = header_sender
        set acl_c1  = No verifiable address in message headers
        message     = X-Sender-Verify-Failed: $acl_c1
        log_message = $acl_c1



   # enforce a message-size limit
   deny message     = Message size $message_size is larger than limit of MESSAGE_SIZE_LIMIT
       condition   = ${if >{$message_size}{MESSAGE_SIZE_LIMIT}{yes}{no}}




# --- BEGIN EXISCAN configuration ---

# Do not scan messages submitted from our own hosts
# and locally submitted messages. Since the DATA ACL
# is not called for messages not submitted via SMTP
# protocols, we do not need to check for an empty
# host field.
#
accept hosts = 127.0.0.1:+relay_from_hosts


   # Reject messages that have serious MIME errors.
   #
   deny message     = Serious MIME defect detected ($demime_reason)
        demime      = *
        condition   = ${if >{$demime_errorlevel}{2}{1}{0}}
        delay       = 20s



   # Unpack MIME containers and reject file extensions used by worms.
   # This calls the demime condition again, but it will return cached results.
   # Note that the extension list may be incomplete.
   #
   deny message     = We do not accept ".$found_extension" attachments here.
        demime      = bat:btm:cmd:com:cpl:dll:exe:lnk:msi:pif:prf:\
                       reg:scr:vbs:url
        delay       = 20s



# Invoke SpamAssassin to obtain $spam_score and $spam_report.
# Add appropriate headers to the message, and if it turns out
# to be spam, stall and pretend to reject the message.
# Note that even though the "spam" condition is called several
# times, the results are cached, and only the first call invokes
# SpamAssassin.

   warn message     = X-Spam-Score: $spam_score
        spam        = mail:true


   warn message     = X-Spam-Status: $spam_report
        spam        = mail:true


   accept
        spam        = mail
        control     = fakereject
        logwrite    = :main: Classified as spam (score $spam_score)
        logwrite    = :reject: SPAM: $spam_report
        delay       = 20s



   # If the message is not flagged as spam, accept it normally.
   # However, stall if we previously generated a warning in $acl_c1.
   accept
        logwrite    = :main: Classified as ham (score $spam_score)
        delay       = ${if def:acl_c1 {20s}{0s}}