The check_mail ruleset for sendmail 8.8 does not deal with some kind of addresses, e.g.,
<@relay.host:spammer@spam.site> <spammer%spam.site@relay.host>There is a general problem with this: in <spammer%spam.site@relay.host> the part left from the @ is considered "local" to relay.host , therefore it is generally not ok to require that this part is "valid" in general. Only relay.host itself must be able to deal with this "local" part. However, this format is seldom used in internet e-mail, and spammers just found out that this format may be used to circumvent usual mail filters. Therefore I wrote an experimental ruleset to check all parts of an e-mail address. However, this ruleset should not be used on a production machine! Please have a look at the ruleset and tell me what you think about it. Suggestions, enhancements, etc. are very welcome! (Sorry, only the .m4 version is available because of the options.)
divert(-1) #============================================================ # WARNING: this is an experimental version! # Don't use it on a production machine! # It is intended to provide new features, some of which violate RFCs! # It is supposed as a release for discussion and testing. # This version provides an option to check _all_ part of an address: # _CHECK_ALL_PARTS_ #============================================================ ifndef(`_ERR_MSG_DOM_',define(`_ERR_MSG_DOM_',`"550 This domain is banned, contact your local admin."') ifndef(`_ERR_MSG_USER_',define(`_ERR_MSG_USER_',`"550 You are banned, contact your local admin."') divert(0) VERSIONID(`@(#)check_mail-exp.m4 1.0.0 (Claus Assmann) 1997-12-07') PUSHDIVERT(6) ifdef(`_JUNK_DEFINED_RELAY',, # file containing full e-mail addresses of spammers (for check_mail): # spammer@address.domain "Error-Code Error-Text" # spammer@address.domain SPAMMER # or junk domains (for check_mail, check_relay): # junk.domain "Error-Code Error-Text" # junk.domain JUNK # or IP addresses (for check_relay): # D.X.Y.Z "Error-Code Error-Text" # C.X.Y "Error-Code Error-Text" # B.X "Error-Code Error-Text" # A "Error-Code Error-Text" Kjunk ifelse(_ARG_, `', `dbm -a@JUNK /etc/mail/junk', `_ARG_') define(`_JUNK_DEFINED_MAIL',1) ) ifdef(`_ACCEPT_SOME_', Kaccept ifelse(_ACCEPT_SOME_,`1',`dbm -o -a@ACCEPT /etc/mail/accept',`_ACCEPT_SOME_') ) POPDIVERT divert(2) LOCAL_RULESETS # check for junk domain/spammers Sjunk # lookup domain in database R$*<@$+> $:$1<@$(junk $2$)> # exists? return R$*<@JUNK@JUNK> $@$1<@ _ERR_MSG_DOM_ @JUNK> R$*<@$*@JUNK> $@$1<@$2@JUNK> # lookup address in database R$*<@$+> $:$1<@$(junk $1@$2 $:$2$)> # exists? return R$*<@SPAMMER@JUNK> $@$1<@ _ERR_MSG_USER_ @JUNK> R$*<@$*@JUNK> $@$1<@$2@JUNK> # remove one subdomain, try again ifdef(`_CHECK_TOPLEVEL_',dnl R$*<@$-.$+> $: $>junk $1<@$3>, R$*<@$-.$-.$+> $: $>junk $1<@$3.$4>) ifdef(`_CHECK_MAIL_IN_RCPT_',Scheckmail,Scheck_mail) # don't check these R<$*@$=w> $@ OK shortcut # idea from Steven Schultz R<> $: <$n @ $(dequote "" $&{client_name} $) > ifdef(`_ACCEPT_SOME_',dnl # accept some stupid stuff R<$*@$+> $:<$1@$(accept $2 $:$2$)> R<$*@$*@ACCEPT> $@ OK # addition from Yar Tikhiy R<$*@$+> $:<$1@$(accept $1@$2 $:$2$)> R<$*@$*@ACCEPT> $@ OK) ifdef(`_CHECK_FROM_',dnl # mark address R$* $:<@>$1 # is the syntax ok (uses <> and no dot at the end?) R<@><$*@$*$~.> $:<$1@$2$3> # mark still there: error... R<@>$* $#error $@ 5.1.8 $: 550 illegal MAIL FROM $1, # remove at least the dot... R<$*@$*.> <$1@$2>) R$* $: $>3 $1 canonify ifdef(`_CHECK_LOCAL_', # another proposal from Steven Schultz R$- $1 <@ $(dequote "" $&{client_name} $)>, R$- $@ OK) # check FROM part R$* $: $>checkfrom $>3 $1 canonify and check from define(`COMMENT', ifdef(`_DNSRELAY_', `', `#'))dnl # is client_name a real host name? COMMENT`'R$* $: $(dequote "" $&{client_name} $) COMMENT`'R[$+] $#error $@ 4.1.8 $: 451 unresolvable relay host name [$1], check your setup. undefine(`COMMENT')dnl ifdef(`_MAPS_RBL_', R$* $: $(dequote "" $&{client_addr} $) R$-.$-.$-.$- $: $[ $4.$3.$2.$1.rbl.maps.vix.com $] R$-.$-.$-.$-.rbl.maps.vix.com. $#error $@ 4.1.8 $: 451 no access from [$4.$3.$2.$1] see http://mail-abuse.org/rbl/) Scheckfrom R$- $@ ok local host # no host without a . in the FQHN ? R$*<@$->$* $#error $@ 5.1.8 $: 551 invalid host name $2, check your configuration. undefine(`COMMENT')dnl define(`COMMENT', ifdef(`_IP_LOOKUP_', `', `#'))dnl # lookup IP address (reverse mapping available?) COMMENT`'R$*<@[$-.$-.$-.$-]>$* $: $1 < @ $[ [ $2.$3.$4.$5 ] $] > $6 ifdef(`_CHECKREGEX_', # check address against checkregex R$* $: $(checkregex $1 $) R@MATCH $#error $@ 5.1.8 $: 550 spam address. go away.) # copy the result of the lookup R$* $:$1 $| $1 # now remove the dot R$* $| $*<@$*.>$* $: $1 $| $2<@$3>$4 # and check the database R$* $| $*<@$*>$* $: $1 $| $>junk $2<@$3> # match: return given error code (rhs of map) R$* $| $*<@$*@JUNK>$* $#error $@ 5.7.1 $: $3 # restore original value (after canonicalization by ruleset 3) R$* $| $* $: $1 undefine(`COMMENT')dnl define(`COMMENT', ifdef(`_DNSVALID_', `', `#'))dnl # this is dangerous! no real name COMMENT`'R$*<@$*$~P>$* $#error $@ 4.1.8 $: 451 unresolvable host name $2$3, check your setup. undefine(`COMMENT')dnl ifdef(`_CHECK_ALL_PARTS_', # retry for the rest R<@$*>:$+ $: $>checkfrom $>3 $2 R$+<@$*>$* $: $(dequote "" $1 $)<@$2>$3 R$+$=O$+<@$*>$* $: $>checkfrom $>3 $1$2$3)