10.3 Anti-UCE

By default Postfix comes ready to support Real Time Black-Hole Lists (RBLs) and it also has a very effective mechanism for using regular expressions at the MTA level to block undesirable mail which is unique. While some people make a political issue out of this, it's a fact that UCE (a.k.a. spam) steals bandwidth and system resources from the receiver without their consent. With Postfix it can be stopped using the following capabilities:

    Header filtering 
    header_checks = regexp:/etc/postfix/header_checks 
    header_checks = pcre:/etc/postfix/header_checks
    
    Client name/address restrictions 
    smtpd_client_restrictions = hash:/etc/postfix/access, reject_maps_rbl 
    

Here's a sample of what could be done with regexp:/etc/postfix/header_checks.

/^Subject: C:\\CoolProgs\\Pretty Park\.exe/  REJECT
# Disallow sender-specified routing. This is a must if you relay mail
# for other domains.
/[%!@].*@/                              550 Directed routing rejected

# Postmaster is OK, that way they can talk to us about how to fix their problem.
/^postmaster@.*$/                       OK

# Protect your outgoing majordomo exploders
/^(.*)-outgoing@(.*)$/!/^owner-.*/      550 Use ${1}@${2} instead of the outgoing address
	

The next example we showed above was called PCRE (Perl Compatible Regular Expressions) which is a library available from ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/. This is actually a patch written by Andrew McNamara. Here's a quote from Andrew which describes what it does:

" I've written [code] to add a regexp map type. It utilises the PCRE library (Perl Compatible Regular Expressions), which can be obtained from: ftp://ftp.cus.cam.ac.uk/pub/software/programs/pcre/ You will need to add -DHAS_PCRE and a -I for the PCRE header to CCARGS, and add the path to the PCRE library to AUXLIBS, for example: make -f Makefile.init makefiles 'CCARGS=-DHAS_PCRE -I../../pcre-2.08' \ 'AUXLIBS=../../pcre-2.08/libpcre.a'. One possible use is to add a line to main.cf: smtpd_recipient_restrictions = pcre:/opt/postfix/etc/smtprecipient The regular expressions are read from the file specified and compiled - a sample regexp file for this usage is included in the patch. Any feedback is appreciated (from Wietse in particular :-). Have fun."

Here's a quote from Wietse which describes some modifications he effected to Andrew's code:

" [I've changed the code so that it can be used for other Postfix table lookups, not just for junk mail control. In particular, regular expressions in canonical tables could be very useful. For the sake of robustness, I have disabled the matching of partial addresses (user@, domain, user, @domain) that is normally done with Postfix access control tables, canonical maps and virtual maps. As a side effect, PCRE maps can only match user@domain strings, so that regexps cannot be used for local alias database lookups. That would be a security exposure anyway -- Wietse.]"

Here's a sample of PCRE usage:

# Protect your outgoing majordomo exploders
#
/^(?!owner-)(.*)-outgoing@(connect.com.au)$/    550 Use ${1}@${2} instead of the outgoing address


# Bounce friend@whatever, except when whatever is our domain (you would
# be better just bouncing all friend@ mail - this is just an example).
#
/^friend@(?!somewhere.com).*$/         550 Stick it $0

# A multi-line response
#
/^turkey@whereever.com$/
 550 This user is an odd one. You really don't want to send mail to them
 as it only makes their head spin.

Here is an actual message from the Postfix daemon which shows what happens when a sending MTA has been blocked properly with the access file parameter:

    From MAILER-DAEMON@uguessit.com Sun Nov 14 15:56:01 1999
    Date: Sat, 13 Nov 1999 14:26:19 -0500 (EST)
    From: Mail Delivery System <MAILER-DAEMON@uguessit.com>
    To: Postmaster <postmaster@uguessit.com>
    Subject: Postfix SMTP server: errors from gatesite.nikoil.ru[194.84.92.1]
    Transcript of session follows.
     Out: 220 server.uguessit.com ESMTP Postfix (Postfix-19990906-pl06)
     In:  HELO nikoil.ru
     Out: 250 server.uguessit.com
     In:  MAIL From:<sandy@admiralkirk.com>
     Out: 250 Ok
     In:  RCPT To:<user@uguessit.com>
     Out: 550 <gatesite.nikoil.ru[194.84.92.1]>: Client host rejected: Mail from
         your domain is rejected as a known spam source
     In:  RSET
     Out: 250 Ok
     In:  QUIT
     Out: 221 Bye
     
    No message was collected successfully.
    

In this case what had happened was an initial spam run took place where a message got through. The system administrator tested and verified that the sending MTA was an open relay and then added the MTA to the access file with a 550 message. Within minutes this second attempt came in and was blocked.

    smtpd_client_restrictions = permit_mynetworks, reject_unknown_client
    Require HELO (EHLO) command 
    smtpd_helo_required = yes
    

A lot of the spam tools out there do not send a proper HELO after connecting. Proper MTAs do... this is an elegant way to differentiate between the two.

    #HELO (EHLO) hostname restrictions 
    
    smtpd_helo_restrictions = reject_invalid_hostname
    
    #Sender address restrictions 
    
    smtpd_sender_restrictions = reject_unknown_sender_domain 
    
    #Recipient address restrictions 
    
    smtpd_recipient_restrictions = 
    						permit_mynetworks, 
						check_relay_domains, 
						reject_unauth_destination, 
						check_recipient_access hash:/etc/postfix/access
    

Additional Restrictions

    #ETRN command restrictions 
    
    smtpd_etrn_restrictions = permit_mynetworks, reject
    
    #Generic restrictions
    
    permit 
    #Permit the request. This restriction is useful at the end of a restriction list, to make the default policy explicit. 
    
    reject 
    #Reject the request. This restriction is useful at the end of a restriction list, to make the default policy explicit. The reject_code configuration parameter specifies the response code to rejected requests (default: 554). 
    
    reject_unauth_pipelining 
    #Reject the request when the client sends SMTP commands ahead of time without knowing that Postfix actually supports SMTP command pipelining. This stops mail from bulk mail software that improperly uses SMTP command pipelining to speed up deliveries. 
    
    Additional UCE control parameters
    
    maps_rbl_domains = rbl.maps.vix.com, dul.maps.vix.com
    relay_domains 
    #This parameter controls the behavior of the check_relay_domains and reject_unauth_destination restrictions that can appear as part of a recipient address restriction list.
 

Here is an actual message blocked by a match on dul.maps.vix.com:

    From MAILER-DAEMON@uguessit.com Sun Nov 14 16:01:40 1999
    Date: Sun, 26 Sep 1999 03:31:04 -0400 (EDT)
    From: Mail Delivery System <MAILER-DAEMON@uguessit.com>
    To: Postmaster <postmaster@uguessit.com>
    Subject: Postfix SMTP server: errors from unknown[171.212.122.86]
     
    Transcript of session follows.
     
     Out: 220 server.uguessit.com ESMTP Postfix (Postfix-19990906-pl05)
     In:  HELO systems-pc
     Out: 250 server.uguessit.com
     In:  MAIL FROM:<familiesc@aol.com>
     Out: 250 Ok
     In:  RCPT TO:<user@uguessit.com>
     Out: 554 Service unavailable; [171.212.122.86] blocked using dul.maps.vix.com
     In:  QUIT
     Out: 221 Bye
     
    No message was collected successfully.
    

In this example the sender is using an MTA direct from an AOL dial-up port. As was mentioned previously in this document there is no reason for a dial-up user without an MX record or PTR which matches their name to be sending mail directly from an MTA. In this case the DUL has worked perfectly. For more information on the DUL, the RBL or UCE in general see the following resources:

The most important thing you need to understand about how Postfix implements its block/allow algorithms is to keep one thing foremost in your mind. In every case the first match is the only one that counts so you must make absolutely sure that if you need to allow certain domains permissive access and block others selectively you must make sure the “allow” match takes place prior to any possible match for a block.