#! /bin/sh -e

# This kludge of a shell script warps a sendmail.cf produced from a .mc file
#   to report some spam to the Distributed Checksum Clearinghouse (DCC)
#   in addition to rejecting it.
#
# Mail that is rejected by a sendmail access_db is reported via dccm to 
#   a DCC server as extremely bulky.
#   Note that the error messages in the access_db file must start with "DCC:"
#   or they will be ignored by this mechanism.
#
# This script should be run in the sendmail cf/cf directory, and given a list
#   of .mc files, as in
#	cd cf/cf
#	.../misc/hackmc -AROT ../m4/cf.m4 local.mc > local.cf

# It seems to work on sendmail.cf generated for sendmail versions 8.11
#   through 8.12.11.  There is no guarantee that it will work with other
#   versions.  You must compare the result of this script with the unmodified
#   sendmail.cf.


# This script "denatures" RCS keywords in its output so that revisions of
#   the resulting sendmail.cf can be archived with RCS without losing
#   the original RCS lines from the Sendmail organization.

# In addition to sending mail blacklisted by the sendmail access_db to
#   the DCC, the following can also be turned on:

#   -A	send mail with bogus Mail_From domain names to the DCC instead of
#	only rejecting it.  This risks reporting legitimate mail from systems
#	with temporarily broken DNS values as spam, and then later rejecting
#	it when the DNS values are fixed and it is retransmitted.

#   -R	silently discard unauthorized relay attempts after reporting them
#	to the DCC.  This mechanism also implies -f to ensure that relayed
#	attempts do not leak if dccm is not running.

#   -r	reject unauthorized relay attempts after reporting them
#	to the DCC.  This mechanism also implies -f to ensure that relayed
#	attempts do not leak if dccm is not running.

#   -D	add a local rule that rejects mail from SMTP clients
#	without reverse DNS and reports the mail as spam to the DCC.
#	Note that this has a fairly high false positive rate.

#   -O	modify the sendmail rules to treat access_db "OK" and "RELAY"
#	or "Spam:...FRIEND" entries as whitelisting the message.

#   -M	modify the sendmail rules generated by FEATURE(dnsbl) or
#	Feature(enhdnsbl) so that mail that is rejected by DNS blacklists is 
#	reported via dccm to a DCC server as extremely bulky.

#   -T	modify the sendmail rules to trust (whitelist) mail from users
#	authenticated with an SMTP AUTH TRUST_AUTH_MECH() mechanism or from
#	SMTP clients with certificates verified with START TLS.
#	If STMP-AUTH used, TRUST_AUTH_MECH must be set in the .mc file and 
#	sendmail must be built with SASL or otherwise have working SMTP auth.

#   -f	if dccm fails, reject mail with a temporary failure status code
#	instead of passing it.  This changes the default FEATURE(dcc)
#	parameters; see dcc.m4.

#   -m m4
#	specifies the path to the m4 program as well as any m4 args
#	such as `hackmc -m4 "/usr/bin/m4 -D_CF_DIR_=/usr/share/sendmail/cf/"`



# Copyright (c) 2004 by Rhyolite Software
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
# BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
# SOFTWARE.
# Rhyolite Software DCC 1.2.74-1.33 $Revision$

USAGE="`basename $0`: [-xfARrDOMT] [-m m4] file1.mc file2.mc ..."
M4=m4
DNS1='#'
DNS2='#'
RELAY='#'
NOTSPAM=
AUTH=
# fail temporarily if dccm is not running.
#	Add F=T to reject mail when dccm is dead,
#	but only if there is not already an F=x setting in the FEATURE() macro.
TEMPFAIL='#'
TEMPFAIL0='/F=/!s/S=[^ ,]*/&, F=T/'
RDNS=
DNSBL=
# remove extra quotes
CLEANUP='#'
CLEANUP0='/TODCC/s/""//'

while getopts "xm:fARrDOMT" c; do
    case $c in
	x) set -x;;
	m) M4="$OPTARG";;
	f) TEMPFAIL=$TEMPFAIL0;;
	A)
	    DNS1='s/$#error $@ \([.0-9]*\) $: "\(5.*[Dd]omain name required.*\)/$# $(macro {dcc_isspam} $@ "\1 \2"  Sent to DCC" $) TODCC/'
	    DNS2='s/$#error $@ \([.0-9]*\) $: "\(5.*Domain of sender.*\)/$# $(macro {dcc_isspam} $@ "\1 \2"  Sent to DCC" $) TODCC/'
	    CLEANUP=$CLEANUP0
	    ;;
	R)
	    RELAY='s/$#error $@ [.0-9]* $: "5[.0-9 ]*\(Relaying denied.*\)/$# $(macro {dcc_isspam} $@ "DISCARD: \1"  Sent to DCC" $) TODCC/'
	    TEMPFAIL=$TEMPFAIL0
	    CLEANUP=$CLEANUP0
	    ;;
	r)
	    RELAY='s/$#error $@ [.0-9]* $: "5[.0-9 ]*\(Relaying denied.*\)/$# $(macro {dcc_isspam} $@ "REJECT: \1"  Sent to DCC" $) TODCC/'
	    TEMPFAIL=$TEMPFAIL0
	    CLEANUP=$CLEANUP0
	    ;;
	D) RDNS=yes
	    ;;
	M) DNSBL='/^# DNS based IP address spam list/,/^$/s/$#error $@ \(5[.0-9]*\) $: "\(.*\)/$@ $(macro {dcc_isspam} $@ "\1 \2"  Sent to DCC" $) TODCC/'
	    ;;
	T) AUTH=yes
	    ;;
	O) NOTSPAM='s/^R<\$={Accept}> *<*\$\*>*		*[^	]*/& $(macro {dcc_notspam} $@ $1 $)/'
	    ;;
	*) echo 1>&2 "$USAGE"; exit 1;;
    esac
done
shift `expr $OPTIND - 1 || true`


# deal with -D and -T
(
if test "$RDNS" = yes -o "$AUTH" = yes; then
    echo LOCAL_RULESETS
fi
if test "$RDNS" = yes; then
	cat <<'EOF'
SLocal_check_relay
R$*			$: <$&{client_resolve}> $1
R<FAIL> $*		$# $(macro {dcc_isspam} $@ "SMTP client "$&{client_addr}" has no reverse DNS name" $) TODCC
R<$*> $*		$: $2

EOF
fi

# deal with -T
if test "$AUTH" = yes; then
	cat <<'EOF'
SLocal_check_mail
# mail from an SMTP client with a verified TLS cert is not spam for dccm
R$*			$: <$&{verify}> $1
R<OK> $*		$: $(macro {dcc_notspam} $@ STARTTLS verified $) <> $1
R<$*> $*		$: <$&{auth_type}> $2
# mail authenticated with SMTP AUTH for relaying is also not spam for dccm
R<$={TrustAuthMech}> $*	$: $(macro {dcc_notspam} $@ authenticated $) <> $2
R<$*> $*		$: $2

EOF
fi
   ) | $M4 $* -								\
    | sed -e 's/\$\(Id:.*\)\$/\1/' -e 's/\$\(Revision:.*\)\$/\1/'	\
	    -e "$DNS1" -e "$DNS2" -e "$RELAY" -e "$NOTSPAM" -e "$DNSBL"	\
	    -e '/^Xdcc/{' -e "$TEMPFAIL" -e '}'				\
	    -e '# always add the access.db hook'			\
	    -e '/^R<$={Accept}>/a\
R<DCC:$*> $*		$# $(macro {dcc_isspam} $@ $1": Sent to DCC" $) TODCC' \
	    -e "$CLEANUP"
