HOME
Andrew Schwabe's Blog : Qmail

Qmail rcptcheck patch posted

It has come to my attention that the checkrcpt patch for qmail seems to have disappeared from earth.  So here is my copy of the patch, reposted along with some other helpful files.

Note: This is for qmail+vpopmail only.  You should be running supervise, and a "run" file is supplied that has the appropriate config for this patch.

No warranty expressed, offerred, blah blah blah.  Use it at your own risk.

http://job2.iexp.com/qmail_patch/


vchkrcpt.sh - my kick butt script for vpopmail

This script is for those who have build a qmail+vpopmail server (including qmr and some of the other distributions).

This script also requires the vchkrcpt patch, which you may need to mangle to work with your config.  It seems that this patch fell through the cracks and didn't get into most of the main distributions.  It is a terrible shame, because almost all default qmail setups have a severe security issue:  they accept all email for a domain, and if an account doesn't exist, they bounce it later.  This is terrible, what with the age of SpamCop and other "spam trap" and realtime black hole lists out there. 

Ponder this batman:  Somebody spams you at your domain with a bad recipient address like not_there@yourcompany.com and forges the sender to be some spamtrap like qazwsx123@spamcop.net.  If your server accepts all mail for "yourcompany.com" and then bounces it later, then it will accept this message.  It will then generate a bounce message, which it sends to the forged email address.  In this scenario, somebody else spammed you, but you sent email to a spam trap, which often automatically blacklists your mail server.  nice, huh...

SO... the point of that little bunny trail is that you shouldn't accept email when it is addressed to a non-existant mailbox.  Thats where vchkrcpt comes in.  Patch your qmail so you can use it (not covered here), and once you can support it, use my script.

And here it is in all its goodness and tested wonder.  Quick warranty and disclaimer:  don't be dumb.  edit this for your server and tested it on a non-production server.  If you don't do these things, its nobody's fault but your own for crashing your mail server.

#!/bin/bash
#
# Andrew's kick-ass chkrcpt script for qmail+vpopmail
# Author: aschwabe@gmail.com
#
# This script makes qmail check for the existance of a user before email is accepted.
# Will check vpopmail accounts, and look for .qmail* files to determine real accounts.
# Logs all checks in the /var/log/maillog, and then refers to it to throttle spammers.

set -- $RECIPIENT
set -- $SENDER
MY_TEST=""

#echo `whoami`

if [ "$RECIPIENT" == "" ]; then
    # uncomment these lines and the "echo" lines throughout to test at the command line
    #echo "sender:"
    #read SENDER
    #echo "recipient:"
    #read RECIPIENT
    exit 100
fi

# email sender addresses explicitly allowed:
allowed[1]="override@yourcompany.comiexp.com"
for index in 1 2
do
  if [ "$SENDER" == "${allowed[index]}" ]; then
        /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : ok (sender explicitly allowed: ${allowed[index]})"
        exit 0
  fi
done

USERPART=`echo "$RECIPIENT" |cut -d@ -f1`
DOMAIN=`echo "$RECIPIENT" |cut -d@ -f2`

# We reject any emails with more than one @ sign
THIRDPART=`echo "$RECIPIENT" |cut -d@ -f3`
if [ "$THIRDPART" != "" ]; then

  #echo "Invalid username - multiple or no @ signs"
  /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : illegal username"
  exit 100

fi
# We reject any emails with ! in the name
EXCL_CHECK=`echo $USERPART |grep '!' >/dev/null 2>&1; echo $?|grep "1" > /dev/null 2>&1 ; echo $?`
if [ "$EXCL_CHECK" == "1" ]; then

  #echo "Invalid username"
  /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : illegal username"
  exit 100

fi

# We reject any emails with a percent sign in the name
PERCENT_CHECK=`echo $USERPART |grep '%' >/dev/null 2>&1; echo $?|grep "1" > /dev/null 2>&1 ; echo $?`
if [ "$PERCENT_CHECK" == "1" ]; then

  #echo "Invalid username"
  /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : illegal username"
  exit 100

fi

# We reject if this sender has had 3 or more bad recipients recently
if [ "$SENDER" != "" ]; then

    SPAMMER_CHECK=`tail -n300 /var/log/maillog|grep vchkrcpt|grep $SENDER |grep 'bounce-no-mailbox'|wc -l`
    #echo 'result of spammer check:'
    #echo $SPAMMER_CHECK

    if [ "$SPAMMER_CHECK" -gt "2" ]; then
        #echo "Too many invalid recipients.  Mail will not be accepted."
        /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : spammer=bounce-no-mailbox"
        exit 100
    fi
fi

# check for valid account
MY_TEST=`/home/vpopmail/bin/vuserinfo -d $RECIPIENT |/usr/bin/head -n1`

if [ "$MY_TEST" == "" ] || [ "${MY_TEST:0:7}" == "no such" ]; then

  # it wasn't a real pop user, so check if its an alias
  MY_TEST=`/home/vpopmail/bin/valias $RECIPIENT|/usr/bin/head -n1`

  # if the address has a dash in it, re-build a default alias address
  USER_ALIAS_CHECK=`echo $USERPART |grep '\-' >/dev/null 2>&1; echo $?|grep "1" > /dev/null 2>&1 ; echo $?`
  if [ "$USER_ALIAS_CHECK" == "1" ]; then
    ALIAS_USERPART=`echo $USERPART | cut -d - -f1`
    ALIAS_USERPART=$ALIAS_USERPART"-default"
    ALIAS_RECIPIENT=$ALIAS_USERPART"@"$DOMAIN
    MY_TEST2=`/home/vpopmail/bin/valias $ALIAS_RECIPIENT|/usr/bin/head -n1`
  else
    MY_TEST2=""
  fi

  if [ "$MY_TEST" != "" ]; then

    # this is a valid alias
    #echo "valid alias"
    /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : alias ok"
    exit 0

  elif [ "$MY_TEST2" != "" ]; then

    # this is a valid user alias
    #echo "valid user-alias"
    /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : user-alias ok"
    exit 0

  else

    # look for a .qmail-default file, and see it if has a real account in it

    DEFAULT_RECIPIENT="default@"$DOMAIN
    #echo "default recipient = $DEFAULT_RECIPIENT"

    DEFAULT_BOUNCE=`/home/vpopmail/bin/valias $DEFAULT_RECIPIENT | /usr/bin/head -n1|grep bounce-no-mailbox \
        >/dev/null 2>&1; echo $? |grep "1" > /dev/null 2>&1 ; echo $?`
    #echo "DEFAULT_BOUNCE result = $DEFAULT_BOUNCE"

    if [ "$DEFAULT_BOUNCE" == "1" ]; then
      #echo ".qmail-default = bounce"
      #echo "invalid recipient"
      /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : catchall=bounce-no-mailbox"
      exit 100
    fi
    DEFAULT_DELETE=`/home/vpopmail/bin/valias $DEFAULT_RECIPIENT | /usr/bin/head -n1|grep ' delete' \
        >/dev/null 2>&1; echo $? |grep "1" > /dev/null 2>&1 ; echo $?`
    #echo "DEFAULT_DELETE result = $DEFAULT_DELETE"

    if [ "$DEFAULT_DELETE" == "1" ]; then
      #echo ".qmail-default = delete"
      #echo "invalid recipient"
      /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : catchall=delete"
      exit 100
    fi

    MY_TEST=`/home/vpopmail/bin/valias $DEFAULT_RECIPIENT | /usr/bin/head -n1`
    #echo "ALIAS DEFAULT result = $MY_TEST"

    if [ "$MY_TEST" == "" ]; then

      #echo "invalid recipient (no default alias)"
      /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : catchall=none"
      exit 100

    else

      #echo "valid recipient (.qmail-default)"
      /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : catchall=ok"
      exit 0

    fi

  fi
else

  #echo "valid mailbox"
  /usr/bin/logger -p mail.info -t vchkrcpt "mail from <$SENDER> to <$RECIPIENT> : account ok"
  exit 0

fi

 

Authenticating web applications agains vpopmail + qmail

We run qmail + vpopmail + spamassassin for email.  it is relatively nice, however the spamassassin config uses qmail-scanner and has a global configuration for multiple domains.  sooo... I wanted to build a web based app where users could manage a whitelist of email addresses to go into the global config.

The first challenge: I dont want to have to add new accounts for everybody that wants access to the whitelist, so I better re-use what security info is available.

Turns out that you can get lots of useful information about a domain in vpopmail by running this command:

/home/vpopmail/bin/vuserinfo -a postmaster@yourdomain.com

This will pull out the postmaster password and a bunch of other useful things.  For now at least, the password is all we need.

Now, choose your technology to write your web app, but use something that can execute local commands.  In this case, we have ColdFusion running on Redhat Enterprise Linux, so we can use the <cfexecute> tag to run the above command, capture the output, which includes the password, and scrape it to pull out the info we want.  Now all we do is compare what the user entered to the real password, and you are done!

So, once this was all put together, users authenticate to my web app by using their domain name and vpopmail postmaster password.

Hope this was helpful.  Feel free to ask me questions if you have similar problems and need help.