Using milter-regex to dump invalid HELOs under Sendmail

A couple of years ago, I was singing the praises of milter-greylist. While greylisting is still effective, in the past year I’ve noticed an increase in the number of spammers, especially this year, that utilize more RFC compliant spamming tools that resend on temporary failure. To combat that, I have recently started using milter-regex to reject on, among other things, invalid HELO strings.

Apr 27 13:07:27 example.com sendmail[24945]: k3RK7Rhe024945: Milter:
  helo=friend, reject=554 5.7.1 Malformed HELO

Installing and configuring milter_regex has an old school feel. The latest version wouldn’t build, so I finally compiled version 1.5. Afterwards, a number of manual steps are necessary to get things up and running.

[jasonb@example.com milter-regex-1.5]$ make -f Makefile.linux
gcc -g -c milter-regex.c
gcc -g -c eval.c
gcc -g -c strlcpy.c
yacc -d parse.y
gcc -g -c y.tab.c
gcc -o milter-regex milter-regex.o eval.o strlcpy.o y.tab.o -lmilter -lpthread
nroff -Tascii -mandoc milter-regex.8 > milter-regex.cat8
# su
# cp milter-regex /usr/local/bin
# mkdir /var/milter-regex
# chown smmsp:root /var/milter-regex
# /usr/local/bin/milter-regex -u smmsp -p /var/milter-regex/milter-regex.sock
# touch /etc/milter-regex.conf

Next, edit your /etc/mail/sendmail.mc and ensure that you include the right milter line. I placed mine above my milter-greylist milter in the same format suggested by the documentation for milter-regex.

INPUT_MAIL_FILTER(`milter-regex',
`S=unix:/var/milter-regex/milter-regex.sock', `T=S:30s,R:2m')

Next you can add some expressions to match things. The first example below simply matches the common ‘public’ HELO. The second matches any HELO that contains only a dotted quad without the required opening and closing brackets.

reject "For all!"
helo /^public$/
reject "No."
helo /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/

Finally, a modified copy of an init.d script for milter-greylist works just fine on Fedora Core 1.

#!/bin/sh
#  init file for milter-regex
#
# chkconfig: - 85 50
# description: Milter Regex Daemon
#
# processname: /usr/local/bin/milter-regex
# config: /etc/milter-regex.conf

# source function library
. /etc/init.d/functions

socket="/var/milter-regex/milter-regex.sock"
user="smmsp"
OPTIONS="-u $user -p $socket"
RETVAL=0
prog="Milter-Regex"

start() {
        echo -n $"Starting $prog: "
        if [ $UID -ne 0 ]; then
                RETVAL=1
                failure
        else
                daemon /usr/local/bin/milter-regex $OPTIONS
                RETVAL=$?
                [ $RETVAL -eq 0 ] && touch /var/lock/subsys/milter-regex
        fi;
        echo
        return $RETVAL
}

stop() {
        echo -n $"Stopping $prog: "
        if [ $UID -ne 0 ]; then
                RETVAL=1
                failure
        else
                killproc /usr/local/bin/milter-regex
                RETVAL=$?
                [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/milter-regex
        fi;
        echo
        return $RETVAL
}

restart(){
        stop
        start
}

condrestart(){
    [ -e /var/lock/subsys/milter-regex ] && restart
    return 0
}

case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart)
        restart
        ;;
  condrestart)
        condrestart
        ;;
  status)
        status milter-regex
        RETVAL=$?
        ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart}"
        RETVAL=1
esac

exit $RETVAL

You can now pattern match all kinds of strings to permanently reject, silently fail, or simply discard.

     24  helo=209.160.41.78, reject=554 5.7.1 No.
     28  helo=209.160.41.77, reject=554 5.7.1 No.
     63  helo=192.168.1.200, reject=554 5.7.1 No.
    638  helo=localhost, reject=554 5.7.1 It's me!
    803  helo=207.36.208.156, reject=451 4.7.1 It's you!

Update, August 1st. I added a connection check to see if the remote MTA’s rDNS appears to be from a dynamic subscriber range.

Increasingly, the helo check has been failing to prevent a significant amount of spam, especially attachment spam, from being rejected by Sendmail. Worse, the attachment spam is littered with random works, making it counterproductive to train dspam with it.

Fortunately, the rDNS reject is working extremely well. Dynamic ranges seem to be so plentiful, my half dozen DNSBLs are missing quite a few ranges. Spammers cannot easily hide the fact that their bot networks operate primarily from compromised Windows machines using dynamic subscriber IP ranges, making this kind of rejection highly effective.

Post a Comment

Your email is never shared. Required fields are marked *

*
*