From fe01e850a8271f3dc25a72e2143fef816623d03b Mon Sep 17 00:00:00 2001 From: Mario Preksavec Date: Fri, 17 May 2019 23:20:52 +0700 Subject: network/opendmarc: Added (DMARC milter and library). Signed-off-by: Willy Sudiarto Raharjo --- network/opendmarc/README | 17 + network/opendmarc/README.SLACKWARE | 26 ++ network/opendmarc/doinst.sh | 26 ++ network/opendmarc/opendmarc.SlackBuild | 162 +++++++ network/opendmarc/opendmarc.info | 10 + .../opendmarc/patches/fix-python-interpreter.diff | 11 + network/opendmarc/patches/ticket137.patch | 51 +++ network/opendmarc/patches/ticket146.patch | 110 +++++ network/opendmarc/patches/ticket153.patch | 35 ++ network/opendmarc/patches/ticket159.patch | 77 ++++ network/opendmarc/patches/ticket180.patch | 280 ++++++++++++ network/opendmarc/patches/ticket182.patch | 18 + network/opendmarc/patches/ticket183.patch | 13 + network/opendmarc/patches/ticket184.patch | 17 + network/opendmarc/patches/ticket193.patch | 193 ++++++++ network/opendmarc/patches/ticket203.patch | 26 ++ network/opendmarc/patches/ticket204.patch | 31 ++ network/opendmarc/patches/ticket205.patch | 38 ++ network/opendmarc/patches/ticket207.patch | 49 +++ network/opendmarc/patches/ticket208.patch | 116 +++++ network/opendmarc/patches/ticket212.patch | 18 + network/opendmarc/patches/ticket227.patch | 40 ++ network/opendmarc/patches/z00_ticket138_v3.patch | 83 ++++ .../z01_changeSubjectFailureReport_v2.patch | 27 ++ .../patches/z02_content-description.patch | 28 ++ .../patches/z03_reportDestVerificationV2.patch | 487 +++++++++++++++++++++ .../z04_moreHeadersFailureReportVsBeta1.patch | 352 +++++++++++++++ .../patches/z06_use_envdomain_SPF_logging.patch | 13 + network/opendmarc/slack-desc | 19 + 29 files changed, 2373 insertions(+) create mode 100644 network/opendmarc/README create mode 100644 network/opendmarc/README.SLACKWARE create mode 100644 network/opendmarc/doinst.sh create mode 100644 network/opendmarc/opendmarc.SlackBuild create mode 100644 network/opendmarc/opendmarc.info create mode 100644 network/opendmarc/patches/fix-python-interpreter.diff create mode 100644 network/opendmarc/patches/ticket137.patch create mode 100644 network/opendmarc/patches/ticket146.patch create mode 100644 network/opendmarc/patches/ticket153.patch create mode 100644 network/opendmarc/patches/ticket159.patch create mode 100644 network/opendmarc/patches/ticket180.patch create mode 100644 network/opendmarc/patches/ticket182.patch create mode 100644 network/opendmarc/patches/ticket183.patch create mode 100644 network/opendmarc/patches/ticket184.patch create mode 100644 network/opendmarc/patches/ticket193.patch create mode 100644 network/opendmarc/patches/ticket203.patch create mode 100644 network/opendmarc/patches/ticket204.patch create mode 100644 network/opendmarc/patches/ticket205.patch create mode 100644 network/opendmarc/patches/ticket207.patch create mode 100644 network/opendmarc/patches/ticket208.patch create mode 100644 network/opendmarc/patches/ticket212.patch create mode 100644 network/opendmarc/patches/ticket227.patch create mode 100644 network/opendmarc/patches/z00_ticket138_v3.patch create mode 100644 network/opendmarc/patches/z01_changeSubjectFailureReport_v2.patch create mode 100644 network/opendmarc/patches/z02_content-description.patch create mode 100644 network/opendmarc/patches/z03_reportDestVerificationV2.patch create mode 100644 network/opendmarc/patches/z04_moreHeadersFailureReportVsBeta1.patch create mode 100644 network/opendmarc/patches/z06_use_envdomain_SPF_logging.patch create mode 100644 network/opendmarc/slack-desc (limited to 'network') diff --git a/network/opendmarc/README b/network/opendmarc/README new file mode 100644 index 000000000000..ba50fb5889bc --- /dev/null +++ b/network/opendmarc/README @@ -0,0 +1,17 @@ +OpenDMARC is a free open source software implementation of the DMARC +specification. + +Enable MySQL support: WITH_MYSQL=yes ./opendmarc.SlackBuild + +Optional dependency (will be autodetected): libspf2 + +You must have a opendmarc user to run this script: + + # groupadd -g 362 opendmarc + # useradd -u 362 -d /var/run/opendmarc -s /bin/false -g opendmarc opendmarc + +And if you have postfix installed, add it to the group: + + # usermod -a -G opendmarc postfix + +More information post-install can be found in README.SLACKWARE diff --git a/network/opendmarc/README.SLACKWARE b/network/opendmarc/README.SLACKWARE new file mode 100644 index 000000000000..ad1a492ec3dd --- /dev/null +++ b/network/opendmarc/README.SLACKWARE @@ -0,0 +1,26 @@ +Starting the daemon on boot +--------------------------- + +You may wish to add these lines to /etc/rc.d/rc.local to start the service: + + if [ -x /etc/rc.d/rc.opendmarc ]; then + /etc/rc.d/rc.opendmarc start + fi + +You may also add these lines to /etc/rc.d/rc.local_shutdown: + + if [ -x /etc/rc.d/rc.opendmarc ]; then + /etc/rc.d/rc.opendmarc stop + fi + +Remember to give executable permission to /etc/rc.d/rc.local_shutdown: + + chmod 0755 /etc/rc.d/rc.local_shutdown + +Hooking into postfix +-------------------- + +Make sure that the DMARC milter is declared after the DKIM milter in /etc/postfix/main.cf + +smtpd_milters = unix:/var/run/opendkim/opendkim.sock, unix:/var/run/opendmarc/opendmarc.sock +non_smtpd_milters = unix:/var/run/opendkim/opendkim.sock, unix:/var/run/opendmarc/opendmarc.sock diff --git a/network/opendmarc/doinst.sh b/network/opendmarc/doinst.sh new file mode 100644 index 000000000000..f7eaa733f48d --- /dev/null +++ b/network/opendmarc/doinst.sh @@ -0,0 +1,26 @@ +config() { + NEW="$1" + OLD="$(dirname $NEW)/$(basename $NEW .new)" + # If there's no config file by that name, mv it over: + if [ ! -r $OLD ]; then + mv $NEW $OLD + elif [ "$(cat $OLD | md5sum)" = "$(cat $NEW | md5sum)" ]; then + # toss the redundant copy + rm $NEW + fi + # Otherwise, we leave the .new copy for the admin to consider... +} + +preserve_perms() { + NEW="$1" + OLD="$(dirname $NEW)/$(basename $NEW .new)" + if [ -e $OLD ]; then + cp -a $OLD ${NEW}.incoming + cat $NEW > ${NEW}.incoming + mv ${NEW}.incoming $NEW + fi + config $NEW +} + +preserve_perms etc/rc.d/rc.opendmarc.new +config etc/opendmarc.conf.new diff --git a/network/opendmarc/opendmarc.SlackBuild b/network/opendmarc/opendmarc.SlackBuild new file mode 100644 index 000000000000..174778264129 --- /dev/null +++ b/network/opendmarc/opendmarc.SlackBuild @@ -0,0 +1,162 @@ +#!/bin/sh + +# Slackware build script for opendmarc + +# Copyright 2019 Mario Preksavec, Zagreb, Croatia +# All rights reserved. +# +# Redistribution and use of this script, with or without modification, is +# permitted provided that the following conditions are met: +# +# 1. Redistributions of this script must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +PRGNAM=opendmarc +VERSION=${VERSION:-1.3.2} +BUILD=${BUILD:-1} +TAG=${TAG:-_SBo} + +if [ -z "$ARCH" ]; then + case "$( uname -m )" in + i?86) ARCH=i586 ;; + arm*) ARCH=arm ;; + *) ARCH=$( uname -m ) ;; + esac +fi + +CWD=$(pwd) +TMP=${TMP:-/tmp/SBo} +PKG=$TMP/package-$PRGNAM +OUTPUT=${OUTPUT:-/tmp} + +if [ "$ARCH" = "i586" ]; then + SLKCFLAGS="-O2 -march=i586 -mtune=i686" + LIBDIRSUFFIX="" +elif [ "$ARCH" = "i686" ]; then + SLKCFLAGS="-O2 -march=i686 -mtune=i686" + LIBDIRSUFFIX="" +elif [ "$ARCH" = "x86_64" ]; then + SLKCFLAGS="-O2 -fPIC" + LIBDIRSUFFIX="64" +else + SLKCFLAGS="-O2" + LIBDIRSUFFIX="" +fi + +# Bail if user or group isn't valid on your system +if ! grep ^$PRGNAM: /etc/passwd 2>&1 > /dev/null; then + cat << EOF + + You must have a $PRGNAM user to run this script + + # groupadd -g 362 $PRGNAM + # useradd -u 362 -d /var/run/$PRGNAM -s /bin/false -g $PRGNAM $PRGNAM + +EOF + + if grep ^postfix: /etc/passwd 2>&1 > /dev/null; then + cat << EOF + + You might want to add postfix user to the group + + # usermod -a -G $PRGNAM postfix + +EOF + fi + exit +fi + +case ${WITH_MYSQL,,} in + y) SQLARGS=--with-sql-backend ;; + *) SQLARGS= ;; +esac + +set -e + +rm -rf $PKG +mkdir -p $TMP $PKG $OUTPUT +cd $TMP +rm -rf $PRGNAM-$VERSION +tar xvf $CWD/$PRGNAM-$VERSION.tar.gz +cd $PRGNAM-$VERSION +chown -R root:root . +find -L . \ + \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \ + -o -perm 511 \) -exec chmod 755 {} \; -o \ + \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \ + -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \; + +# http://batleth.sapienti-sat.org/projects/opendmarc/ +# ... and a few more +for i in $CWD/patches/* ; do patch -p1 <$i ; done + +autoreconf -vif + +CFLAGS="$SLKCFLAGS" \ +CXXFLAGS="$SLKCFLAGS" \ +./configure \ + --prefix=/usr \ + --libdir=/usr/lib${LIBDIRSUFFIX} \ + --sysconfdir=/etc \ + --localstatedir=/var \ + --mandir=/usr/man \ + --docdir=/usr/doc/$PRGNAM-$VERSION \ + --enable-static=no \ + --with-spf \ + $SQLARGS \ + --build=$ARCH-slackware-linux + +make +make install DESTDIR=$PKG + +# Install init script +install -D -m0755 -oroot -groot contrib/init/generic/$PRGNAM $PKG/etc/rc.d/rc.$PRGNAM.new +sed -e "s|^\(prefix=\).*|\1|" \ + -e "s|^\(exec_prefix=\).*|\1/usr|" \ + -e "s|/etc/sysconfig/opendmarc|/etc/default/opendmarc|" \ + -i $PKG/etc/rc.d/rc.$PRGNAM.new + +# Configure defaults +install -D -m0644 -oroot -groot $PRGNAM/$PRGNAM.conf.sample $PKG/etc/$PRGNAM.conf.new +sed -e "s|^# \(AuthservID\) .*|\1 HOSTNAME|" \ + -e "s|^# \(HistoryFile\) .*|\1 /var/run/$PRGNAM/$PRGNAM.dat|" \ + -e "s|^# \(Socket\) .*|\1 unix:/var/run/$PRGNAM/$PRGNAM.sock|" \ + -e "s|^# \(SPFIgnoreResults\) .*|\1 true|" \ + -e "s|^# \(SPFSelfValidate\) .*|\1 true|" \ + -e "s|^# \(Syslog\) .*|\1 true|" \ + -e "s|^# \(UMask\) .*|\1 007|" \ + -e "s|^# \(UserID\) .*|\1 $PRGNAM:$PRGNAM|" \ + -i $PKG/etc/$PRGNAM.conf.new + +# Home directory for runtime data +mkdir -p $PKG/var/run/$PRGNAM +chown $PRGNAM:$PRGNAM $PKG/var/run/$PRGNAM + +find $PKG -print0 | xargs -0 file | grep -e "executable" -e "shared object" | grep ELF \ + | cut -f 1 -d : | xargs strip --strip-unneeded 2> /dev/null || true + +find $PKG/usr/man -type f -exec gzip -9 {} \; +for i in $( find $PKG/usr/man -type l ) ; do ln -s $( readlink $i ).gz $i.gz ; rm $i ; done + +mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION +cp -a RELEASE_NOTES $PKG/usr/doc/$PRGNAM-$VERSION +cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild +cat $CWD/README.SLACKWARE > $PKG/usr/doc/$PRGNAM-$VERSION/README.SLACKWARE + +mkdir -p $PKG/install +cat $CWD/slack-desc > $PKG/install/slack-desc +cat $CWD/doinst.sh > $PKG/install/doinst.sh + +cd $PKG +/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.${PKGTYPE:-tgz} diff --git a/network/opendmarc/opendmarc.info b/network/opendmarc/opendmarc.info new file mode 100644 index 000000000000..061a20ed5ee9 --- /dev/null +++ b/network/opendmarc/opendmarc.info @@ -0,0 +1,10 @@ +PRGNAM="opendmarc" +VERSION="1.3.2" +HOMEPAGE="http://www.trusteddomain.org/opendmarc/" +DOWNLOAD="https://sourceforge.net/projects/opendmarc/files/opendmarc-1.3.2.tar.gz" +MD5SUM="2b4e9b8be7fe61800515cef1d7e6a905" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +REQUIRES="" +MAINTAINER="Mario Preksavec" +EMAIL="mario at slackware dot hr" diff --git a/network/opendmarc/patches/fix-python-interpreter.diff b/network/opendmarc/patches/fix-python-interpreter.diff new file mode 100644 index 000000000000..e81a3328e347 --- /dev/null +++ b/network/opendmarc/patches/fix-python-interpreter.diff @@ -0,0 +1,11 @@ +Does not need to be forwarded. Upstream uses FreeBSD. +Index: opendmarc-1.0.1+dfsg/contrib/rddmarc/dmarcfail.py +=================================================================== +--- opendmarc-1.0.1+dfsg.orig/contrib/rddmarc/dmarcfail.py 2012-11-18 00:08:28.105858463 -0500 ++++ opendmarc-1.0.1+dfsg/contrib/rddmarc/dmarcfail.py 2012-11-18 00:09:10.041857224 -0500 +@@ -1,4 +1,4 @@ +-#!/usr/local/bin/python ++#!/usr/bin/python + # $Header: /home/johnl/hack/dmarc/RCS/dmarcfail.py,v 1.1 2012/07/12 03:59:29 johnl Exp $ + # parse DMARC failure reports, add it to the mysql database + # optional arguments are names of files containing ARF messages, diff --git a/network/opendmarc/patches/ticket137.patch b/network/opendmarc/patches/ticket137.patch new file mode 100644 index 000000000000..df6c062152b5 --- /dev/null +++ b/network/opendmarc/patches/ticket137.patch @@ -0,0 +1,51 @@ +From afc1615946cd127d9ea28e0892934251c6a00a84 Mon Sep 17 00:00:00 2001 +From: "Murray S. Kucherawy" +Date: Sat, 4 Mar 2017 08:03:22 -0800 +Subject: [PATCH] Fix bug #137: Handle base64 inside AR tokens that are values. + Problem reported by Joseph Coffland. + +--- + RELEASE_NOTES | 2 ++ + opendmarc/opendmarc-ar.c | 14 +++++++++++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +Index: opendmarc/RELEASE_NOTES +=================================================================== +--- opendmarc.orig/RELEASE_NOTES 2018-12-17 01:38:44.570329334 -0500 ++++ opendmarc/RELEASE_NOTES 2018-12-17 01:40:21.062333399 -0500 +@@ -3,6 +3,10 @@ + This listing shows the versions of the OpenDMARC package, the date of + release, and a summary of the changes in that release. + ++ 1.4.0 2017/??/?? ++ Fix bug #137: Handle base64 inside AR tokens that are values. ++ Problem reported by Joseph Coffland. ++ + 1.3.2 2016/12/19 + Feature request #86: Change meaning of "RequiredHeaders" such that + header validity is always checked, but messages are only +Index: opendmarc/opendmarc/opendmarc-ar.c +=================================================================== +--- opendmarc.orig/opendmarc/opendmarc-ar.c 2018-12-17 01:38:44.570329334 -0500 ++++ opendmarc/opendmarc/opendmarc-ar.c 2018-12-17 01:38:44.566329334 -0500 +@@ -602,7 +602,19 @@ + ar->ares_result[n - 1].result_props = r; + + prevstate = state; +- state = 9; ++ if (c < ntoks - 1 && tokens[c + 1][1] == '\0') ++ { ++ if (tokens[c + 1][0] == ';') ++ state = 2; ++ else if (tokens[c + 1][0] == '=') ++ r--; ++ else ++ state = 9; ++ } ++ else ++ { ++ state = 9; ++ } + + break; + } diff --git a/network/opendmarc/patches/ticket146.patch b/network/opendmarc/patches/ticket146.patch new file mode 100644 index 000000000000..c7f6f5748b1f --- /dev/null +++ b/network/opendmarc/patches/ticket146.patch @@ -0,0 +1,110 @@ +diff --git a/reports/opendmarc-import.8.in b/reports/opendmarc-import.8.in +index 8f55848..4e854ac 100644 +--- a/reports/opendmarc-import.8.in ++++ b/reports/opendmarc-import.8.in +@@ -12,8 +12,6 @@ reads per-message data recorded by an instance of + and inserts it into an SQL database, for later use by + .B opendmarc-reports(8) + to generate aggregate reports. +- +-Records are read from standard input. + .SH OPTIONS + .TP + .I --dbhost=hostname +@@ -44,6 +42,9 @@ the environment variable is not set. + .I --help + Prints a help message and terminates. + .TP ++.I --input=file ++Reads from the named file instead of from standard input (the default). ++.TP + .I --verbose + Increase the amount of verbosity written to standard output. + .TP +diff --git a/reports/opendmarc-import.in b/reports/opendmarc-import.in +index 5a28f2f..cccbace 100755 +--- a/reports/opendmarc-import.in ++++ b/reports/opendmarc-import.in +@@ -35,11 +35,14 @@ my $def_dbuser = "opendmarc"; + my $def_dbpasswd = "opendmarc"; + my $def_dbport = "3306"; + my $def_interval = "86400"; ++my $def_inputfh = *STDIN; + my $dbhost; + my $dbname; + my $dbuser; + my $dbpasswd; + my $dbport; ++my $inputfile; ++my $inputfh; + + my $dbscheme = "@SQL_BACKEND@"; + +@@ -326,6 +329,7 @@ sub usage + print STDERR "\t--dbpasswd=passwd database password [$def_dbpasswd]\n"; + print STDERR "\t--dbport=port database port [$def_dbport]\n"; + print STDERR "\t--dbuser=user database user [$def_dbuser]\n"; ++ print STDERR "\t--input=file input file [STDIN]\n"; + print STDERR "\t--help print help and exit\n"; + print STDERR "\t--verbose verbose output\n"; + print STDERR "\t--version print version and exit\n"; +@@ -337,6 +341,7 @@ my $opt_retval = &Getopt::Long::GetOptions ('dbhost=s' => \$dbhost, + 'dbpasswd=s' => \$dbpasswd, + 'dbport=s' => \$dbport, + 'dbuser=s' => \$dbuser, ++ 'input=s' => \$inputfile, + 'help!' => \$helponly, + 'verbose!' => \$verbose, + 'version!' => \$showversion, +@@ -428,6 +433,24 @@ if ($verbose) + print STDERR "$progname: started at " . localtime() . "\n"; + } + ++ ++if (!defined($inputfile)) ++{ ++ $inputfh = $def_inputfh; ++} ++else ++{ ++ open($inputfh, "<", $inputfile) or die "$progname: unable to open $inputfile: $!\n"; ++ if ($verbose) ++ { ++ print STDERR "$progname: opened file $inputfile\n" ++ } ++} ++if (!flock($inputfh, LOCK_SH)) ++{ ++ print STDERR "$progname: warning: unable to establish read lock\n"; ++} ++ + my $dbi_dsn = "DBI:" . $dbscheme . ":database=" . $dbname . + ";host=" . $dbhost . ";port=" . $dbport; + +@@ -447,13 +470,10 @@ if ($verbose) + # Read history file from stdin. + # + ++ + $lineno = 0; +-if (!flock(STDIN, LOCK_SH)) +-{ +- print STDERR "$progname: warning: unable to establish read lock\n"; +-} + +-while () ++while (<$inputfh>) + { + $lineno++; + +@@ -592,6 +612,10 @@ if (defined($jobid)) + update_db(); + } + ++if (defined($inputfile)) ++{ ++ close($inputfh); ++} + # + # all done! + # diff --git a/network/opendmarc/patches/ticket153.patch b/network/opendmarc/patches/ticket153.patch new file mode 100644 index 000000000000..1193409cb7d6 --- /dev/null +++ b/network/opendmarc/patches/ticket153.patch @@ -0,0 +1,35 @@ +Description: do not report same dkim result multiple times + in the same record object +URL: https://sf.net/p/opendmarc/tickets/153/ +Author: Tomki +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +Index: opendmarc-1.3.2/reports/opendmarc-reports.in +=================================================================== +--- opendmarc-1.3.2.orig/reports/opendmarc-reports.in 2017-03-13 19:01:56.496961757 -0400 ++++ opendmarc-1.3.2/reports/opendmarc-reports.in 2017-03-13 19:01:56.492961756 -0400 +@@ -703,6 +703,7 @@ + exit(1); + } + ++ my %dkim_domain_result_cache = (); + while ($dbi_a = $dbi_d->fetchrow_arrayref()) + { + undef $dkimdomain; +@@ -716,11 +717,15 @@ + $dkimresult = $dbi_a->[1]; + } + +- + if (!defined($dkimdomain)) + { + next; + } ++ if (defined($dkim_domain_result_cache{$dkimdomain}{$dkimresult})) ++ { ++ next; # no duplicate per-record auth_result dkim sections ++ } ++ $dkim_domain_result_cache{$dkimdomain}{$dkimresult}++; + + switch ($dkimresult) + { diff --git a/network/opendmarc/patches/ticket159.patch b/network/opendmarc/patches/ticket159.patch new file mode 100644 index 000000000000..13a90436144e --- /dev/null +++ b/network/opendmarc/patches/ticket159.patch @@ -0,0 +1,77 @@ +diff --git a/configure.ac b/configure.ac +index 255c449..27d7cd4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -492,6 +492,7 @@ AC_OUTPUT([ Makefile + reports/opendmarc-expire.8 + reports/opendmarc-import + reports/opendmarc-import.8 ++ reports/opendmarc-importstats + reports/opendmarc-importstats.8 + reports/opendmarc-params + reports/opendmarc-params.8 +diff --git a/reports/opendmarc-importstats b/reports/opendmarc-importstats +deleted file mode 100755 +index 839a871..0000000 +--- a/reports/opendmarc-importstats ++++ /dev/null +@@ -1,26 +0,0 @@ +-#!/bin/sh +-## +-## Copyright (c) 2012, The Trusted Domain Project. All rights reserved. +-## +-## opendmarc-importstats -- import opendmarc output to MySQL +-## +-## This is intended to be used via a crontab. If import is successful, +-## this code exits quietly so there's no output. If it fails, it does +-## "ls -l" on the temporary file, so that cron generates mail to whever +-## ran the job. +- +-## setup +-statsdb="/var/tmp/dmarc.dat" +-# OPENDMARC_PASSWORD="password"; export OPENDMARC_PASSWORD +- +-if [ -s $statsdb ] +-then +- mv $statsdb ${statsdb}.OLD.$$ +- +- if opendmarc-import < ${statsdb}.OLD.$$ +- then +- rm ${statsdb}.OLD.$$ +- else +- ls -l ${statsdb}.OLD.$$ +- fi +-fi +diff --git a/reports/opendmarc-importstats.in b/reports/opendmarc-importstats.in +new file mode 100755 +index 0000000..3a28ee3 +--- /dev/null ++++ b/reports/opendmarc-importstats.in +@@ -0,0 +1,27 @@ ++#!/bin/sh ++## ++## Copyright (c) 2012, The Trusted Domain Project. All rights reserved. ++## ++## opendmarc-importstats -- import opendmarc output to MySQL ++## ++## This is intended to be used via a crontab. If import is successful, ++## this code exits quietly so there's no output. If it fails, it does ++## "ls -l" on the temporary file, so that cron generates mail to whever ++## ran the job. ++ ++## setup ++statsdb="`grep ^HistoryFile @sysconfdir@/opendmarc.conf | sed 's/^HistoryFile\s\+//'`" ++[ -z "$statsdb" ] && exit 0 ++# OPENDMARC_PASSWORD="password"; export OPENDMARC_PASSWORD ++ ++if [ -s "$statsdb" ] ++then ++ mv "$statsdb" "${statsdb}.OLD.$$" ++ ++ if opendmarc-import < "${statsdb}.OLD.$$" ++ then ++ rm "${statsdb}.OLD.$$" ++ else ++ ls -l "${statsdb}.OLD.$$" ++ fi ++fi diff --git a/network/opendmarc/patches/ticket180.patch b/network/opendmarc/patches/ticket180.patch new file mode 100644 index 000000000000..cd38c39c383b --- /dev/null +++ b/network/opendmarc/patches/ticket180.patch @@ -0,0 +1,280 @@ +diff --git a/opendmarc/opendmarc-config.h b/opendmarc/opendmarc-config.h +index 7ba394b..28f605e 100644 +--- a/opendmarc/opendmarc-config.h ++++ b/opendmarc/opendmarc-config.h +@@ -36,6 +36,7 @@ struct configdef dmarcf_config[] = + { "IgnoreHosts", CONFIG_TYPE_STRING, FALSE }, + { "IgnoreMailFrom", CONFIG_TYPE_STRING, FALSE }, + { "MilterDebug", CONFIG_TYPE_INTEGER, FALSE }, ++ { "OverrideMLM", CONFIG_TYPE_STRING, FALSE }, + { "PidFile", CONFIG_TYPE_STRING, FALSE }, + { "PublicSuffixList", CONFIG_TYPE_STRING, FALSE }, + { "RecordAllMessages", CONFIG_TYPE_BOOLEAN, FALSE }, +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index ba04312..07e089d 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -168,6 +168,7 @@ struct dmarcf_config + char * conf_ignorelist; + char ** conf_trustedauthservids; + char ** conf_ignoredomains; ++ struct list * conf_overridemlm; + }; + + /* LIST -- basic linked list of strings */ +@@ -1221,6 +1222,18 @@ dmarcf_config_load(struct config *data, struct dmarcf_config *conf, + if (str != NULL) + dmarcf_mkarray(str, &conf->conf_ignoredomains); + ++ str = NULL; ++ (void) config_get(data, "OverrideMLM", &str, sizeof str); ++ if (str != NULL) ++ { ++ if (!dmarcf_loadlist(str, &conf->conf_overridemlm)) ++ { ++ fprintf(stderr, ++ "%s: can't load override MLM list from %s: %s\n", ++ progname, str, strerror(errno)); ++ } ++ } ++ + (void) config_get(data, "AuthservIDWithJobID", + &conf->conf_authservidwithjobid, + sizeof conf->conf_authservidwithjobid); +@@ -2982,30 +2995,45 @@ mlfi_eom(SMFICTX *ctx) + case DMARC_POLICY_REJECT: /* Explicit reject */ + aresult = "fail"; + +- if (conf->conf_rejectfail && random() % 100 < pct) ++ if (conf->conf_overridemlm != NULL && ++ (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || ++ (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) + { +- snprintf(replybuf, sizeof replybuf, +- "rejected by DMARC policy for %s", pdomain); +- +- status = dmarcf_setreply(ctx, DMARC_REJECT_SMTP, +- DMARC_REJECT_ESC, replybuf); +- if (status != MI_SUCCESS && conf->conf_dolog) ++ if (conf->conf_dolog) + { +- syslog(LOG_ERR, "%s: smfi_setreply() failed", +- dfc->mctx_jobid); ++ syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", ++ dfc->mctx_jobid, dfc->mctx_fromdomain); + } +- +- ret = SMFIS_REJECT; +- result = DMARC_RESULT_REJECT; ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_OVRD_MAILING_LIST; + } +- +- if (conf->conf_copyfailsto != NULL) ++ else + { +- status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); +- if (status != MI_SUCCESS && conf->conf_dolog) ++ if (conf->conf_rejectfail && random() % 100 < pct) ++ { ++ snprintf(replybuf, sizeof replybuf, ++ "rejected by DMARC policy for %s", pdomain); ++ ++ status = dmarcf_setreply(ctx, DMARC_REJECT_SMTP, ++ DMARC_REJECT_ESC, replybuf); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_setreply() failed", ++ dfc->mctx_jobid); ++ } ++ ++ ret = SMFIS_REJECT; ++ result = DMARC_RESULT_REJECT; ++ } ++ ++ if (conf->conf_copyfailsto != NULL) + { +- syslog(LOG_ERR, "%s: smfi_addrcpt() failed", +- dfc->mctx_jobid); ++ status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_addrcpt() failed", ++ dfc->mctx_jobid); ++ } + } + } + +@@ -3014,30 +3042,45 @@ mlfi_eom(SMFICTX *ctx) + case DMARC_POLICY_QUARANTINE: /* Explicit quarantine */ + aresult = "fail"; + +- if (conf->conf_rejectfail && random() % 100 < pct) ++ if (conf->conf_overridemlm != NULL && ++ (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || ++ (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) + { +- snprintf(replybuf, sizeof replybuf, +- "quarantined by DMARC policy for %s", +- pdomain); +- +- status = smfi_quarantine(ctx, replybuf); +- if (status != MI_SUCCESS && conf->conf_dolog) ++ if (conf->conf_dolog) + { +- syslog(LOG_ERR, "%s: smfi_quarantine() failed", +- dfc->mctx_jobid); ++ syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", ++ dfc->mctx_jobid, dfc->mctx_fromdomain); + } +- + ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_QUARANTINE; ++ result = DMARC_RESULT_OVRD_MAILING_LIST; + } +- +- if (conf->conf_copyfailsto != NULL) ++ else + { +- status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); +- if (status != MI_SUCCESS && conf->conf_dolog) ++ if (conf->conf_rejectfail && random() % 100 < pct) ++ { ++ snprintf(replybuf, sizeof replybuf, ++ "quarantined by DMARC policy for %s", ++ pdomain); ++ ++ status = smfi_quarantine(ctx, replybuf); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_quarantine() failed", ++ dfc->mctx_jobid); ++ } ++ ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_QUARANTINE; ++ } ++ ++ if (conf->conf_copyfailsto != NULL) + { +- syslog(LOG_ERR, "%s: smfi_addrcpt() failed", +- dfc->mctx_jobid); ++ status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_addrcpt() failed", ++ dfc->mctx_jobid); ++ } + } + } + +diff --git a/opendmarc/opendmarc.conf.5.in b/opendmarc/opendmarc.conf.5.in +index bdf2550..9ee16ae 100644 +--- a/opendmarc/opendmarc.conf.5.in ++++ b/opendmarc/opendmarc.conf.5.in +@@ -190,6 +190,14 @@ Sets the debug level to be requested from the milter library. The + default is 0. + + .TP ++.I OverrideMLM (string) ++Specifies the path to a file that contains a list of hostnames, IP ++addresses, and/or CIDR expressions identifying hosts that run ++mailing lists. Mails from these systems will be accepted even if ++all DMARC tests fail. Such cases will be reported as "override/ ++reason: MLM" ++ ++.TP + .I PidFile (string) + Specifies the path to a file that should be created at process start + containing the process ID. +diff --git a/opendmarc/opendmarc.conf.sample b/opendmarc/opendmarc.conf.sample +index 97b210f..fbfa49d 100644 +--- a/opendmarc/opendmarc.conf.sample ++++ b/opendmarc/opendmarc.conf.sample +@@ -212,6 +212,17 @@ + # + # MilterDebug 0 + ++## OverrideMLM (path) ++## default (none) ++## ++## Specifies the path to a file that contains a list of hostnames, IP ++## addresses, and/or CIDR expressions identifying hosts that run ++## mailing lists. Mails from these systems will be accepted even if ++## all DMARC tests fail. Such cases will be reported as "override/ ++## reason: MLM" ++# ++# OverrideMLM /usr/local/etc/opendmarc/overrideMLM.conf ++ + ## PidFile path + ## default (none) + ## +diff --git a/opendmarc/opendmarc.h b/opendmarc/opendmarc.h +index c1d6593..f9b1e0b 100644 +--- a/opendmarc/opendmarc.h ++++ b/opendmarc/opendmarc.h +@@ -52,6 +52,12 @@ + #define DMARC_RESULT_ACCEPT 2 + #define DMARC_RESULT_TEMPFAIL 3 + #define DMARC_RESULT_QUARANTINE 4 ++#define DMARC_RESULT_OVRD_FORWARDED 5 ++#define DMARC_RESULT_OVRD_SAMPLED_OUT 6 ++#define DMARC_RESULT_OVRD_TRUSTED_FORWARDER 7 ++#define DMARC_RESULT_OVRD_MAILING_LIST 8 ++#define DMARC_RESULT_OVRD_LOCAL_POLICY 9 ++#define DMARC_RESULT_OVRD_OTHER 10 + + /* prototypes, etc., exported for test.c */ + extern char *progname; +diff --git a/reports/opendmarc-reports.in b/reports/opendmarc-reports.in +index 2da1c31..a489c95 100755 +--- a/reports/opendmarc-reports.in ++++ b/reports/opendmarc-reports.in +@@ -91,6 +91,8 @@ my $ipaddr; + my $fromdomain; + my $envdomain; + my $dkimdomain; ++my $reason; ++my $comment; + + my $repdest; + +@@ -609,6 +611,8 @@ foreach (@$domainset) + while ($dbi_a = $dbi_s->fetchrow_arrayref()) + { + undef $msgid; ++ undef $reason; ++ undef $comment; + + if (defined($dbi_a->[0])) + { +@@ -656,6 +660,12 @@ foreach (@$domainset) + case 1 { $dispstr = "reject"; } + case 2 { $dispstr = "none"; } + case 4 { $dispstr = "quarantine"; } ++ case 5 { $dispstr = "none"; $reason = "forwarded"; } ++ case 6 { $dispstr = "none"; $reason = "sampled_out"; } ++ case 7 { $dispstr = "none"; $reason = "trusted_forwarder"; } ++ case 8 { $dispstr = "none"; $reason = "mailing_list"; } ++ case 9 { $dispstr = "none"; $reason = "local_policy"; $comment = ""; } ++ case 10 { $dispstr = "none"; $reason = "other"; $comment = ""; } + else { $dispstr = "unknown"; } + } + +@@ -697,6 +707,16 @@ foreach (@$domainset) + print $tmpout " $dispstr\n"; + print $tmpout " $align_dkimstr\n"; + print $tmpout " $align_spfstr\n"; ++ if (defined($reason)) ++ { ++ print $tmpout " \n"; ++ print $tmpout " $reason\n"; ++ if (defined($comment)) ++ { ++ print $tmpout " $comment\n"; ++ } ++ print $tmpout " \n"; ++ } + print $tmpout " \n"; + print $tmpout " \n"; + print $tmpout " \n"; diff --git a/network/opendmarc/patches/ticket182.patch b/network/opendmarc/patches/ticket182.patch new file mode 100644 index 000000000000..567115e34475 --- /dev/null +++ b/network/opendmarc/patches/ticket182.patch @@ -0,0 +1,18 @@ +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index 0b3457a..1c0e27a 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -1863,7 +1863,13 @@ mlfi_envfrom(SMFICTX *ctx, char **envfrom) + + if (conf->conf_ignoreauthclients && + dmarcf_getsymval(ctx, "{auth_authen}") != NULL) ++ { ++ if (curconf->conf_dolog) ++ { ++ syslog(LOG_INFO, "ignoring authenticated client, mailfrom=%s", envfrom[0]); ++ } + return SMFIS_ACCEPT; ++ } + + dfc = (DMARCF_MSGCTX) malloc(sizeof(struct dmarcf_msgctx)); + if (dfc == NULL) diff --git a/network/opendmarc/patches/ticket183.patch b/network/opendmarc/patches/ticket183.patch new file mode 100644 index 000000000000..f76ef697f730 --- /dev/null +++ b/network/opendmarc/patches/ticket183.patch @@ -0,0 +1,13 @@ +diff --git a/reports/opendmarc-reports.in b/reports/opendmarc-reports.in +index 5dfa5ee..6826ff7 100755 +--- a/reports/opendmarc-reports.in ++++ b/reports/opendmarc-reports.in +@@ -878,7 +878,7 @@ foreach (@$domainset) + $mailout .= "Content-Type: text/plain;\n"; + $mailout .= "\n"; + $mailout .= "This is a DMARC aggregate report for $domain\n"; +- $mailout .= "generated at " . localtime() . "\n"; ++ $mailout .= "generated at " . strftime("%a, %b %e %Y %H:%M:%S %z (%Z)", localtime()) . "\n"; + $mailout .= "\n"; + $mailout .= "--$boundary\n"; + $mailout .= "Content-Type: application/zip\n"; diff --git a/network/opendmarc/patches/ticket184.patch b/network/opendmarc/patches/ticket184.patch new file mode 100644 index 000000000000..6cdd235dec5d --- /dev/null +++ b/network/opendmarc/patches/ticket184.patch @@ -0,0 +1,17 @@ +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index 2a60b92..07e21a4 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -2859,9 +2859,9 @@ mlfi_eom(SMFICTX *ctx) + dmarcf_dstring_printf(dfc->mctx_afrf, + "This is an authentication " + "failure report for an email " +- "message received from IP\n" +- "%s on %s.\n\n", +- cc->cctx_ipstr, timebuf); ++ "message received from\n" ++ "%s [%s] on %s.\n\n", ++ cc->cctx_host, cc->cctx_ipstr, timebuf); + + dmarcf_dstring_printf(dfc->mctx_afrf, + "--%s:%s\n" diff --git a/network/opendmarc/patches/ticket193.patch b/network/opendmarc/patches/ticket193.patch new file mode 100644 index 000000000000..1ee1911174e9 --- /dev/null +++ b/network/opendmarc/patches/ticket193.patch @@ -0,0 +1,193 @@ +Index: opendmarc/db/Makefile.am +=================================================================== +--- opendmarc.orig/db/Makefile.am 2018-12-17 01:41:11.326335516 -0500 ++++ opendmarc/db/Makefile.am 2018-12-17 01:41:11.318335516 -0500 +@@ -1,3 +1,3 @@ + # Copyright (c) 2012, The Trusted Domain Project. All rights reserved. + +-dist_doc_DATA = README.schema schema.mysql ++dist_doc_DATA = README.schema schema.mysql README.update-db-schema.mysql update-db-schema.mysql +Index: opendmarc/db/README.update-db-schema.mysql +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ opendmarc/db/README.update-db-schema.mysql 2018-12-17 01:41:11.318335516 -0500 +@@ -0,0 +1,8 @@ ++ ++To update your database to the current state use this script like this: ++ ++ mysql -u -p --force < update-db-schema.mysql ++ ++You might receive up to four errors about duplicate keys - this is expected if your database ++already has these keys (because you used the MySQL schema in the db sub-direcory instead of ++the obsolete schema in the reports sub-dirctory). +Index: opendmarc/db/schema.mysql +=================================================================== +--- opendmarc.orig/db/schema.mysql 2018-12-17 01:41:11.326335516 -0500 ++++ opendmarc/db/schema.mysql 2018-12-17 01:41:11.318335516 -0500 +@@ -5,6 +5,7 @@ + + CREATE DATABASE IF NOT EXISTS opendmarc; + USE opendmarc; ++SET TIME_ZONE='+00:00'; + + -- A table for mapping domain names and their DMARC policies to IDs + CREATE TABLE IF NOT EXISTS domains ( +@@ -28,7 +29,7 @@ + pct TINYINT NOT NULL, + locked TINYINT NOT NULL DEFAULT '0', + firstseen TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, +- lastsent TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', ++ lastsent TIMESTAMP NOT NULL DEFAULT '1970-01-01 00:00:01', + + PRIMARY KEY(id), + KEY(lastsent), +Index: opendmarc/db/update-db-schema.mysql +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ opendmarc/db/update-db-schema.mysql 2018-12-17 01:41:11.318335516 -0500 +@@ -0,0 +1,12 @@ ++use opendmarc; ++SET TIME_ZONE="+00:00"; ++ALTER TABLE ipaddr MODIFY COLUMN addr VARCHAR(64) NOT NULL; ++DELETE FROM ipaddr WHERE addr = NULL; ++ALTER TABLE messages MODIFY COLUMN spf TINYINT NOT NULL; ++ALTER TABLE requests ALTER COLUMN locked SET DEFAULT '0'; ++ALTER TABLE requests ALTER COLUMN lastsent SET DEFAULT '1970-01-01 00:00:01'; ++ALTER TABLE requests ADD UNIQUE KEY domain (domain); ++ALTER TABLE requests ADD KEY lastsent (lastsent); ++ALTER TABLE messages ADD KEY date (date); ++ALTER TABLE signatures ADD KEY message (message); ++ +Index: opendmarc/reports/opendmarc-expire.in +=================================================================== +--- opendmarc.orig/reports/opendmarc-expire.in 2018-12-17 01:41:11.326335516 -0500 ++++ opendmarc/reports/opendmarc-expire.in 2018-12-17 01:41:11.318335516 -0500 +@@ -210,6 +210,17 @@ + print STDERR "$progname: connected to database\n"; + } + ++# switch to UTC to have a defined date behaviour ++$dbi_s = $dbi_h->prepare("SET TIME_ZONE='+00:00'"); ++ ++if (!$dbi_s->execute()) ++{ ++ print STDERR "$progname: failed to change to UTC: " . $dbi_h->errstr . "\n"; ++ $dbi_s->finish; ++ $dbi_h->disconnect; ++ exit(1); ++} ++ + # + # Expire messages + # +@@ -340,7 +351,7 @@ + print STDERR "$progname: expiring request data older than $maxage days\n"; + } + +-$dbi_s = $dbi_h->prepare("DELETE FROM requests WHERE lastsent <= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL ? DAY) AND NOT lastsent = '0000-00-00 00:00:00'"); ++$dbi_s = $dbi_h->prepare("DELETE FROM requests WHERE lastsent <= DATE_SUB(CURRENT_TIMESTAMP(), INTERVAL ? DAY) AND NOT lastsent <= '1970-01-01 00:00:01'"); + $rows = $dbi_s->execute($maxage); + if (!$rows) + { +Index: opendmarc/reports/opendmarc-import.in +=================================================================== +--- opendmarc.orig/reports/opendmarc-import.in 2018-12-17 01:41:11.326335516 -0500 ++++ opendmarc/reports/opendmarc-import.in 2018-12-17 01:41:11.322335516 -0500 +@@ -207,20 +207,18 @@ + $envfrom_id = get_table_id($envdomain, "domains"); + $pdomain_id = get_table_id($pdomain, "domains"); + $ipaddr_id = get_table_id($ipaddr, "ipaddr", "addr"); +- $request_id = get_table_id($from_id, "requests", "domain"); + + if (!defined($rep_id) || + !defined($from_id) || + !defined($envfrom_id) || + !defined($pdomain_id) || +- !defined($ipaddr_id) || +- !defined($request_id)) ++ !defined($ipaddr_id)) + { + return; + } + +- $dbi_s = $dbi_h->prepare("INSERT INTO messages (date, jobid, reporter, policy, disp, ip, env_domain, from_domain, spf, align_spf, align_dkim, sigcount) VALUES(FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); +- if (!$dbi_s->execute($received, $jobid, $rep_id, $policy, $action, $ipaddr_id, $envfrom_id, $from_id, $spf, $align_spf, $align_dkim, $sigcount)) ++ $dbi_s = $dbi_h->prepare("INSERT INTO messages (date, jobid, reporter, policy, disp, ip, env_domain, from_domain, policy_domain, spf, align_spf, align_dkim, sigcount) VALUES(FROM_UNIXTIME(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); ++ if (!$dbi_s->execute($received, $jobid, $rep_id, $policy, $action, $ipaddr_id, $envfrom_id, $from_id, $pdomain_id, $spf, $align_spf, $align_dkim, $sigcount)) + { + print STDERR "$progname: failed to insert message: " . $dbi_h->errstr . "\n"; + return; +@@ -278,41 +276,51 @@ + } + $dbi_s->finish; + +- if (get_value("requests", "locked", $request_id) != 1) ++ $dbi_t = $dbi_h->prepare("SELECT id FROM requests WHERE domain = ?"); ++ if (!$dbi_t->execute($from_id)) + { +- if (scalar @rua > 0) ++ print STDERR "$progname: failed to retrieve table ID: " . $dbi_h->errstr . "\n"; ++ return undef; ++ } ++ ++ undef $request_id; ++ while ($dbi_a = $dbi_t->fetchrow_arrayref()) ++ { ++ if (defined($dbi_a->[0])) + { +- $repuri = join(",", @rua); +- $dbi_s = $dbi_h->prepare("UPDATE requests SET repuri = ? WHERE id = ?"); ++ $request_id = $dbi_a->[0]; ++ } ++ } + +- if (!$dbi_s->execute($repuri, $request_id)) +- { +- print STDERR "$progname: failed to update reporting URI for $fdomain: " . $dbi_h->errstr . "\n"; +- $dbi_s->finish; +- return; +- } ++ $dbi_t->finish; + +- $dbi_s->finish; +- } +- else ++ $repuri = join(",", @rua); ++ ++ if (defined($request_id)) ++ { ++ if (get_value("requests", "locked", $request_id) != 1) + { +- $dbi_s = $dbi_h->prepare("UPDATE requests SET repuri = NULL WHERE id = ?"); ++ $dbi_s = $dbi_h->prepare("UPDATE requests SET domain = ?, repuri = ?, adkim = ?, aspf = ?, policy = ?, spolicy = ?, pct = ? WHERE id = ?"); + +- if (!$dbi_s->execute($request_id)) ++ if (!$dbi_s->execute($from_id, $repuri, $adkim, $aspf, $p, $sp, $pct, $request_id)) + { +- print STDERR "$progname: failed to update reporting URI for $fdomain: " . $dbi_h->errstr . "\n"; ++ print STDERR "$progname: failed to update policy data for $fdomain: " . $dbi_h->errstr . "\n"; + $dbi_s->finish; + return; + } +- +- $dbi_s->finish; + } ++ else ++ { ++ print STDERR "$progname: policy data for $fdomain not updated, because they are locked\n"; ++ } ++ } ++ else ++ { ++ $dbi_s = $dbi_h->prepare("insert requests SET domain = ?, repuri = ?, adkim = ?, aspf = ?, policy = ?, spolicy = ?, pct = ?"); + +- $dbi_s = $dbi_h->prepare("UPDATE requests SET adkim = ?, aspf = ?, policy = ?, spolicy = ?, pct = ? WHERE id = ?"); +- +- if (!$dbi_s->execute($adkim, $aspf, $p, $sp, $pct, $request_id)) ++ if (!$dbi_s->execute($from_id, $repuri, $adkim, $aspf, $p, $sp, $pct)) + { +- print STDERR "$progname: failed to update policy data for $fdomain: " . $dbi_h->errstr . "\n"; ++ print STDERR "$progname: failed to insert policy data for $fdomain: " . $dbi_h->errstr . "\n"; + $dbi_s->finish; + return; + } diff --git a/network/opendmarc/patches/ticket203.patch b/network/opendmarc/patches/ticket203.patch new file mode 100644 index 000000000000..8aa5113176dc --- /dev/null +++ b/network/opendmarc/patches/ticket203.patch @@ -0,0 +1,26 @@ +diff --git a/libopendmarc/opendmarc_policy.c b/libopendmarc/opendmarc_policy.c +index 862c449..8048ec3 100644 +--- a/libopendmarc/opendmarc_policy.c ++++ b/libopendmarc/opendmarc_policy.c +@@ -1087,6 +1087,10 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + /* + * A possibly comma delimited list of URI of where to send reports. + */ ++ ++ if (pctx->rua_list != NULL) ++ return DMARC_PARSE_ERROR_BAD_VALUE; ++ + for (xp = vp; *xp != '\0'; ) + { + u_char xbuf[256]; +@@ -1115,6 +1119,10 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + * A possibly comma delimited list of URI of where to send + * MARF reports. + */ ++ ++ if (pctx->ruf_list != NULL) ++ return DMARC_PARSE_ERROR_BAD_VALUE; ++ + for (xp = vp; *xp != '\0'; ) + { + u_char xbuf[256]; diff --git a/network/opendmarc/patches/ticket204.patch b/network/opendmarc/patches/ticket204.patch new file mode 100644 index 000000000000..afbb45ad1350 --- /dev/null +++ b/network/opendmarc/patches/ticket204.patch @@ -0,0 +1,31 @@ +Index: opendmarc/reports/opendmarc-import.in +=================================================================== +--- opendmarc.orig/reports/opendmarc-import.in 2018-12-17 01:41:32.570336411 -0500 ++++ opendmarc/reports/opendmarc-import.in 2018-12-17 01:41:32.566336411 -0500 +@@ -529,7 +529,7 @@ + } + + case "from" { +- $fdomain = $value; ++ $fdomain = lc($value); + } + + case "job" { +@@ -567,7 +567,7 @@ + } + + case "mfrom" { +- $envdomain = $value; ++ $envdomain = lc($value); + } + + case "p" { +@@ -579,7 +579,7 @@ + } + + case "pdomain" { +- $pdomain = $value; ++ $pdomain = lc($value); + } + + case "policy" { diff --git a/network/opendmarc/patches/ticket205.patch b/network/opendmarc/patches/ticket205.patch new file mode 100644 index 000000000000..11089408dcdf --- /dev/null +++ b/network/opendmarc/patches/ticket205.patch @@ -0,0 +1,38 @@ +--- opendmarc-1.3.2/reports/opendmarc-reports.in 2017-02-16 16:15:01.000000000 +0100 ++++ opendmarc-1.3.2_fix/reports/opendmarc-reports.in 2017-02-26 10:43:46.697335371 +0100 +@@ -157,6 +157,7 @@ + print STDERR "\t--keepfiles keep xml files (in local directory)\n"; + print STDERR "\t -n synonym for --test\n"; + print STDERR "\t--nodomain=name omit a report for named domain\n"; ++ print STDERR "\t--skipdomains=file list of domains to omit a report for\n"; + print STDERR "\t--noupdate don't record report transmission\n"; + print STDERR "\t--report-email reporting contact [$repemail]\n"; + print STDERR "\t--report-org reporting organization [$repdom]\n"; +@@ -173,6 +174,19 @@ + # set locale + setlocale(LC_ALL, 'C'); + ++sub loadskipdomains ++{ ++ die "Could not open domains file $_[1]" unless open FILE,"<",$_[1]; ++ while (my $line = ) ++ { ++ $line =~ s/\s*#.*//; ++ $line =~ s/^\s+//; ++ $line =~ s/\s+//; ++ push(@skipdomains, $line); ++ } ++ close FILE; ++} ++ + # parse command line arguments + my $opt_retval = &Getopt::Long::GetOptions ('day!' => \$daybound, + 'dbhost=s' => \$dbhost, +@@ -186,6 +200,7 @@ + 'keepfiles' => \$keepfiles, + 'n|test' => \$testmode, + 'nodomain=s' => \@skipdomains, ++ 'skipdomains=s' => \&loadskipdomains, + 'report-email=s' => \$repemail, + 'report-org=s' => \$repdom, + 'smtp-server=s' => \$smtp_server, diff --git a/network/opendmarc/patches/ticket207.patch b/network/opendmarc/patches/ticket207.patch new file mode 100644 index 000000000000..e86b8c770466 --- /dev/null +++ b/network/opendmarc/patches/ticket207.patch @@ -0,0 +1,49 @@ +Index: opendmarc/reports/opendmarc-reports.in +=================================================================== +--- opendmarc.orig/reports/opendmarc-reports.in 2018-12-17 01:41:49.094337107 -0500 ++++ opendmarc/reports/opendmarc-reports.in 2018-12-17 01:41:49.090337107 -0500 +@@ -65,6 +65,7 @@ + my $forcedomain; + my @skipdomains; + ++my $poldomain; + my $policy; + my $spolicy; + my $policystr; +@@ -441,7 +442,7 @@ + next; + } + +- $dbi_s = $dbi_h->prepare("SELECT repuri, adkim, aspf, policy, spolicy, pct, UNIX_TIMESTAMP(lastsent) FROM requests WHERE domain = ?"); ++ $dbi_s = $dbi_h->prepare("SELECT repuri, adkim, aspf, requests.policy, spolicy, pct, UNIX_TIMESTAMP(lastsent), domains.name FROM requests JOIN messages ON messages.from_domain=requests.domain LEFT JOIN domains ON messages.policy_domain = domains.id WHERE domain = ? GROUP BY policy_domain"); + if (!$dbi_s->execute($domainid)) + { + print STDERR "$progname: can't get reporting URI for domain $domain: " . $dbi_h->errstr . "\n"; +@@ -451,6 +452,7 @@ + } + + undef $repuri; ++ $poldomain=$domain; + + while ($dbi_a = $dbi_s->fetchrow_arrayref()) + { +@@ -482,6 +484,10 @@ + { + $lastsent = $dbi_a->[6]; + } ++ if (defined($dbi_a->[7])) ++ { ++ $poldomain = $dbi_a->[7]; ++ } + } + + $dbi_s->finish; +@@ -564,7 +570,7 @@ + print $tmpout " \n"; + + print $tmpout " \n"; +- print $tmpout " $domain\n"; ++ print $tmpout " $poldomain\n"; + print $tmpout " $adkimstr\n"; + print $tmpout " $aspfstr\n"; + print $tmpout "

$policystr

\n"; diff --git a/network/opendmarc/patches/ticket208.patch b/network/opendmarc/patches/ticket208.patch new file mode 100644 index 000000000000..fc8c6455d82f --- /dev/null +++ b/network/opendmarc/patches/ticket208.patch @@ -0,0 +1,116 @@ +diff -ur opendmarc-1.3.2/opendmarc/opendmarc.c opendmarc-1.3.2_fix/opendmarc/opendmarc.c +--- opendmarc-1.3.2/opendmarc/opendmarc.c 2017-03-04 14:28:39.000000000 +0100 ++++ opendmarc-1.3.2_fix/opendmarc/opendmarc.c 2017-03-27 18:11:14.977304726 +0200 +@@ -168,7 +168,8 @@ + char * conf_ignorelist; + char ** conf_trustedauthservids; + char ** conf_ignoredomains; + struct list * conf_overridemlm; ++ char ** conf_ignorereceivers; + }; + + /* LIST -- basic linked list of strings */ +@@ -1226,6 +1227,11 @@ + if (str != NULL) + dmarcf_mkarray(str, &conf->conf_ignoredomains); + ++ str = NULL; ++ (void) config_get(data, "IgnoreMailTo", &str, sizeof str); ++ if (str != NULL) ++ dmarcf_mkarray(str, &conf->conf_ignorereceivers); ++ + (void) config_get(data, "AuthservIDWithJobID", + &conf->conf_authservidwithjobid, + sizeof conf->conf_authservidwithjobid); +@@ -2015,6 +2021,7 @@ + mlfi_eom(SMFICTX *ctx) + { + _Bool wspf = FALSE; ++ int skiphistory; + int c; + int pc; + int policy; +@@ -3147,7 +3154,34 @@ + ** Record activity in the history file. + */ + +- if (conf->conf_historyfile != NULL && ++ skiphistory = 0; ++ if (conf->conf_ignorereceivers != NULL) ++ { ++ struct dmarcf_header *to = dmarcf_findheader(dfc, "To", 0); ++ if (to != NULL) ++ { ++ char *val = to->hdr_value; ++ while (*val && !skiphistory) ++ { ++ memset(addrbuf, '\0', sizeof addrbuf); ++ strncpy(addrbuf, val, sizeof addrbuf - 1); ++ status = dmarcf_mail_parse(addrbuf, &user, &domain); ++ if (status == 0 && user != NULL && domain != NULL) ++ { ++ snprintf(replybuf, sizeof replybuf - 1, "%s@%s", user, domain); ++ if(dmarcf_match(replybuf, conf->conf_ignorereceivers, TRUE)) ++ { ++ skiphistory = 1; ++ } ++ } ++ while(*val && *val != ',' && *val != ';') ++ ++val; ++ if(*val) ++ ++val; ++ } ++ } ++ } ++ if (!skiphistory && conf->conf_historyfile != NULL && + (conf->conf_recordall || ostatus != DMARC_DNS_ERROR_NO_RECORD)) + { + FILE *f; +diff -ur opendmarc-1.3.2/opendmarc/opendmarc.conf.5.in opendmarc-1.3.2_fix/opendmarc/opendmarc.conf.5.in +--- opendmarc-1.3.2/opendmarc/opendmarc.conf.5.in 2016-12-18 08:50:34.000000000 +0100 ++++ opendmarc-1.3.2_fix/opendmarc/opendmarc.conf.5.in 2017-03-27 17:00:14.424955664 +0200 +@@ -185,6 +185,13 @@ + no mail is ignored. + + .TP ++.I IgnoreMailTo (string) ++Gives a list of mail addresses which aren't entered into the history file. ++This is useful to prevent exchanging single message reports. The ++list should be comma-separated. Matching against this list is ++case-insensitive. The default is an empty list, meaning no mail is ignored. ++ ++.TP + .I MilterDebug (integer) + Sets the debug level to be requested from the milter library. The + default is 0. +diff -ur opendmarc-1.3.2/opendmarc/opendmarc-config.h opendmarc-1.3.2_fix/opendmarc/opendmarc-config.h +--- opendmarc-1.3.2/opendmarc/opendmarc-config.h 2016-12-18 08:50:34.000000000 +0100 ++++ opendmarc-1.3.2_fix/opendmarc/opendmarc-config.h 2017-03-27 17:39:01.727649907 +0200 +@@ -35,6 +35,7 @@ + { "IgnoreAuthenticatedClients", CONFIG_TYPE_BOOLEAN, FALSE }, + { "IgnoreHosts", CONFIG_TYPE_STRING, FALSE }, + { "IgnoreMailFrom", CONFIG_TYPE_STRING, FALSE }, ++ { "IgnoreMailTo", CONFIG_TYPE_STRING, FALSE }, + { "MilterDebug", CONFIG_TYPE_INTEGER, FALSE }, + { "PidFile", CONFIG_TYPE_STRING, FALSE }, + { "PublicSuffixList", CONFIG_TYPE_STRING, FALSE }, +diff -ur opendmarc-1.3.2/opendmarc/opendmarc.conf.sample opendmarc-1.3.2_fix/opendmarc/opendmarc.conf.sample +--- opendmarc-1.3.2/opendmarc/opendmarc.conf.sample 2017-03-04 14:28:39.000000000 +0100 ++++ opendmarc-1.3.2_fix/opendmarc/opendmarc.conf.sample 2017-03-27 17:39:32.594647158 +0200 +@@ -205,6 +205,16 @@ + # + # IgnoreMailFrom example.com + ++## IgnoreMailTo email[,...] ++## default (none) ++## ++## Gives a list of mail addresses which aren't entered into the history file. ++## This is useful to prevent exchanging mutual message reports. The ++## list should be comma-separated. Matching against this list is ++## case-insensitive. The default is an empty list, meaning no mail is ignored. ++# ++# IgnoreMailTo dmarc-ruf@example.com ++ + ## MilterDebug (integer) + ## default 0 + ## diff --git a/network/opendmarc/patches/ticket212.patch b/network/opendmarc/patches/ticket212.patch new file mode 100644 index 000000000000..cf8c01c731ef --- /dev/null +++ b/network/opendmarc/patches/ticket212.patch @@ -0,0 +1,18 @@ +diff --git a/libopendmarc/opendmarc_tld.c b/libopendmarc/opendmarc_tld.c +index 8ac45e8..0c04370 100644 +--- a/libopendmarc/opendmarc_tld.c ++++ b/libopendmarc/opendmarc_tld.c +@@ -134,8 +134,11 @@ + return (errno == 0) ? ENOMEM : errno; + + fp = fopen(path_fname, "r"); +- if (fp == NULL) +- return errno; ++ if (fp == NULL) { ++ ret = errno; ++ opendmarc_hash_shutdown(hashp); ++ return ret; ++ } + + errno = 0; + while (fgets((char *)buf, sizeof buf, fp) != NULL) diff --git a/network/opendmarc/patches/ticket227.patch b/network/opendmarc/patches/ticket227.patch new file mode 100644 index 000000000000..b2786aef6155 --- /dev/null +++ b/network/opendmarc/patches/ticket227.patch @@ -0,0 +1,40 @@ +diff --git a/libopendmarc/opendmarc_policy.c b/libopendmarc/opendmarc_policy.c +index 36412e4..f151fda 100644 +--- a/libopendmarc/opendmarc_policy.c ++++ b/libopendmarc/opendmarc_policy.c +@@ -1058,7 +1058,7 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + *yp = '\0'; + + xp = opendmarc_util_cleanup(xp, xbuf, sizeof xbuf); +- if (xp != NULL || strlen((char *)xp) > 0) ++ if (xp != NULL && strlen((char *)xp) > 0) + { + /* + * Be generous. Accept, for example, "rf=a, aspf=afrf or any +@@ -1100,7 +1100,7 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + *yp = '\0'; + + xp = opendmarc_util_cleanup(xp, xbuf, sizeof xbuf); +- if (xp != NULL || strlen((char *)xp) > 0) ++ if (xp != NULL && strlen((char *)xp) > 0) + { + pctx->rua_list = opendmarc_util_pushargv(xp, pctx->rua_list, + &(pctx->rua_cnt)); +@@ -1132,7 +1132,7 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + *yp = '\0'; + + xp = opendmarc_util_cleanup(xp, xbuf, sizeof xbuf); +- if (xp != NULL || strlen((char *)xp) > 0) ++ if (xp != NULL && strlen((char *)xp) > 0) + { + pctx->ruf_list = opendmarc_util_pushargv(xp, pctx->ruf_list, + &(pctx->ruf_cnt)); +@@ -1159,7 +1159,7 @@ opendmarc_policy_parse_dmarc(DMARC_POLICY_T *pctx, u_char *domain, u_char *recor + *yp = '\0'; + + xp = opendmarc_util_cleanup(xp, xbuf, sizeof xbuf); +- if (xp != NULL || strlen((char *)xp) > 0) ++ if (xp != NULL && strlen((char *)xp) > 0) + { + switch ((int)*xp) + { diff --git a/network/opendmarc/patches/z00_ticket138_v3.patch b/network/opendmarc/patches/z00_ticket138_v3.patch new file mode 100644 index 000000000000..4bcd4f92b4b4 --- /dev/null +++ b/network/opendmarc/patches/z00_ticket138_v3.patch @@ -0,0 +1,83 @@ +diff --git a/opendmarc/opendmarc-config.h b/opendmarc/opendmarc-config.h +index 28f605e..ff4983d 100644 +--- a/opendmarc/opendmarc-config.h ++++ b/opendmarc/opendmarc-config.h +@@ -32,6 +32,7 @@ struct configdef dmarcf_config[] = + { "FailureReportsOnNone", CONFIG_TYPE_BOOLEAN, FALSE }, + { "FailureReportsSentBy", CONFIG_TYPE_STRING, FALSE }, + { "HistoryFile", CONFIG_TYPE_STRING, FALSE }, ++ { "HoldQuarantinedMessages", CONFIG_TYPE_BOOLEAN, FALSE }, + { "IgnoreAuthenticatedClients", CONFIG_TYPE_BOOLEAN, FALSE }, + { "IgnoreHosts", CONFIG_TYPE_STRING, FALSE }, + { "IgnoreMailFrom", CONFIG_TYPE_STRING, FALSE }, +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index 0179f4d..5aade55 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -155,6 +155,7 @@ struct dmarcf_config + _Bool conf_spfselfvalidate; + #endif /* WITH_SPF */ + _Bool conf_ignoreauthclients; ++ _Bool conf_holdquarantinedmessages; + unsigned int conf_refcnt; + unsigned int conf_dnstimeout; + struct config * conf_data; +@@ -1297,6 +1298,10 @@ dmarcf_config_load(struct config *data, struct dmarcf_config *conf, + &conf->conf_recordall, + sizeof conf->conf_recordall); + ++ (void) config_get(data, "HoldQuarantinedMessages", ++ &conf->conf_holdquarantinedmessages, ++ sizeof conf->conf_holdquarantinedmessages); ++ + (void) config_get(data, "IgnoreAuthenticatedClients", + &conf->conf_ignoreauthclients, + sizeof conf->conf_ignoreauthclients); +@@ -3064,7 +3069,8 @@ mlfi_eom(SMFICTX *ctx) + } + else + { +- if (conf->conf_rejectfail && random() % 100 < pct) ++ if (conf->conf_rejectfail && random() % 100 < pct && ++ conf->conf_holdquarantinedmessages) + { + snprintf(replybuf, sizeof replybuf, + "quarantined by DMARC policy for %s", +diff --git a/opendmarc/opendmarc.conf.5.in b/opendmarc/opendmarc.conf.5.in +index 9ee16ae..565e992 100644 +--- a/opendmarc/opendmarc.conf.5.in ++++ b/opendmarc/opendmarc.conf.5.in +@@ -167,6 +167,13 @@ rather periodically imported into a relational database from which the + aggregate reports can be extracted. + + .TP ++.I HoldQuarantinedMessages (Boolean) ++If set to true, causes mail that fails the DMARC tests to get hold ++by the MTA if the purported sender of the message has a policy of ++"quarantine". Does nothing if the policy is either "none" or "reject". ++The default is "true". ++ ++.TP + .I IgnoreAuthenticatedClients (Boolean) + If set, causes mail from authenticated clients (i.e., those that used + SMTP AUTH) to be ignored by the filter. The default is "false". +diff --git a/opendmarc/opendmarc.conf.sample b/opendmarc/opendmarc.conf.sample +index fbfa49d..a2e1da3 100644 +--- a/opendmarc/opendmarc.conf.sample ++++ b/opendmarc/opendmarc.conf.sample +@@ -177,6 +177,15 @@ + # + # HistoryFile /var/run/opendmarc.dat + ++## HoldQuarantinedMessages { true | false } ++## default "true" ++## ++## If set to true, causes mail that fails the DMARC tests to get hold ++## by the MTA if the purported sender of the message has a policy of ++## "quarantine". Does nothing if the policy is either "none" or "reject". ++# ++# HoldQuarantinedMessages true ++ + ## IgnoreAuthenticatedClients { true | false } + ## default "false" + ## diff --git a/network/opendmarc/patches/z01_changeSubjectFailureReport_v2.patch b/network/opendmarc/patches/z01_changeSubjectFailureReport_v2.patch new file mode 100644 index 000000000000..918283791ba6 --- /dev/null +++ b/network/opendmarc/patches/z01_changeSubjectFailureReport_v2.patch @@ -0,0 +1,27 @@ +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index bdad10d..d29036f 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -3005,19 +3005,9 @@ mlfi_eom(SMFICTX *ctx) + dmarcf_dstring_printf(dfc->mctx_afrf, "Date: %s\n", + timebuf); + +- h = dmarcf_findheader(dfc, "subject", 0); +- if (h == NULL) +- { +- dmarcf_dstring_printf(dfc->mctx_afrf, +- "Subject: DMARC failure report for job %s\n", +- dfc->mctx_jobid); +- } +- else +- { +- dmarcf_dstring_printf(dfc->mctx_afrf, +- "Subject: FW: %s\n", +- h->hdr_value); +- } ++ dmarcf_dstring_printf(dfc->mctx_afrf, ++ "Subject: DMARC failure report for %s received from %s\n", ++ dfc->mctx_fromdomain, cc->cctx_host); + + dmarcf_dstring_cat(dfc->mctx_afrf, + "MIME-Version: 1.0\n"); diff --git a/network/opendmarc/patches/z02_content-description.patch b/network/opendmarc/patches/z02_content-description.patch new file mode 100644 index 000000000000..838def9c7b03 --- /dev/null +++ b/network/opendmarc/patches/z02_content-description.patch @@ -0,0 +1,28 @@ +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index 0645bcf..2a60b92 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -2852,6 +2852,7 @@ mlfi_eom(SMFICTX *ctx) + + dmarcf_dstring_printf(dfc->mctx_afrf, + "--%s:%s\n" ++ "Content-Description: Notification\n" + "Content-Type: text/plain\n\n", + hostname, dfc->mctx_jobid); + +@@ -2864,6 +2865,7 @@ mlfi_eom(SMFICTX *ctx) + + dmarcf_dstring_printf(dfc->mctx_afrf, + "--%s:%s\n" ++ "Content-Description: DMARC failure report\n" + "Content-Type: message/feedback-report\n\n", + hostname, dfc->mctx_jobid); + +@@ -2902,6 +2904,7 @@ mlfi_eom(SMFICTX *ctx) + + dmarcf_dstring_printf(dfc->mctx_afrf, + "--%s:%s\n" ++ "Content-Description: Failed message headers\n" + "Content-Type: text/rfc822-headers\n\n", + hostname, dfc->mctx_jobid); + diff --git a/network/opendmarc/patches/z03_reportDestVerificationV2.patch b/network/opendmarc/patches/z03_reportDestVerificationV2.patch new file mode 100644 index 000000000000..6e428b76ce6e --- /dev/null +++ b/network/opendmarc/patches/z03_reportDestVerificationV2.patch @@ -0,0 +1,487 @@ +diff --git b/reports/opendmarc-reports.in a/reports/opendmarc-reports.in +index 43be1ff..fff9f8d 100755 +--- b/reports/opendmarc-reports.in ++++ a/reports/opendmarc-reports.in +@@ -24,6 +24,8 @@ use POSIX; + use MIME::Base64; + use Net::SMTP; + use Time::Local; ++use Net::DNS; ++use Domain::PublicSuffix; + + require DBD::@SQL_BACKEND@; + +@@ -39,7 +41,6 @@ my $showversion = 0; + my $interval; + + my $gen; +-my $uri; + + my $buf; + +@@ -95,8 +96,6 @@ my $dkimdomain; + my $reason; + my $comment; + +-my $repdest; +- + my $smtpstatus; + my $smtpfail; + +@@ -140,6 +139,18 @@ my $smtp; + + my $answer; + ++my $suffix; ++my $publicsuffixlist = "/etc/opendmarc/public_suffix_list.dat"; ++if (-r $publicsuffixlist) { ++ $suffix = Domain::PublicSuffix->new( ++ { 'data_file' => $publicsuffixlist } ++ ); ++} ++else ++{ ++ $suffix = Domain::PublicSuffix->new(); ++} ++ + ### + ### NO user-serviceable parts beyond this point + ### +@@ -172,6 +183,71 @@ sub usage + print STDERR "\t--version print version and exit\n"; + } + ++sub check_size_restriction ++{ ++ my ($destination, $size) = @_; ++ my $report_maxbytes = $report_maxbytes_global; ++ ++ # check for max report size ++ if ($destination =~ m/^(\S+)!(\d{1,15})([kmgt])?$/i) ++ { ++ $destination = $1; ++ $report_maxbytes = $2; ++ if ($3) ++ { ++ my $letter = lc($3); ++ if ($letter eq 'k') ++ { ++ $report_maxbytes = $report_maxbytes * 1024; ++ } ++ if ($letter eq 'm') ++ { ++ $report_maxbytes = $report_maxbytes * 1048576; ++ } ++ if ($letter eq 'g') ++ { ++ $report_maxbytes = $report_maxbytes * (2**30); ++ } ++ if ($letter eq 't') ++ { ++ $report_maxbytes = $report_maxbytes * (2**40); ++ } ++ } ++ ++ if ($size > $report_maxbytes) ++ { ++ return 0; ++ } ++ } ++ return 1; ++} ++ ++sub check_uri ++{ ++ my $uri = URI->new($_[0]); ++ if (!defined($uri) || ++ !defined($uri->scheme) || ++ $uri->opaque eq "") ++ { ++ print STDERR "$progname: can't parse reporting URI for domain $domain\n"; ++ return ""; ++ } ++ # ensure a scheme is present ++ elsif (!defined($uri->scheme)) ++ { ++ if ($verbose >= 2) ++ { ++ print STDERR "$progname: unknown URI scheme in '$repuri' for domain $domain\n"; ++ } ++ return ""; ++ } ++ elsif ($uri->scheme eq "mailto") ++ { ++ return $uri->opaque; ++ } ++ return ""; ++} ++ + # set locale + setlocale(LC_ALL, 'C'); + +@@ -798,86 +874,181 @@ foreach (@$domainset) + print STDERR "$progname: keeping report file \"$repfile\"\n"; + } + ++ if (!open($zipin, $zipfile)) ++ { ++ print STDERR "$progname: can't read zipped report for $domain: $!\n"; ++ next; ++ } ++ my $encoded_report; ++ while (read($zipin, $buf, 60*57)) ++ { ++ $encoded_report .= encode_base64($buf); ++ } ++ close($zipin); ++ my $reportsize = length($encoded_report); ++ ++ my $repdest = ""; ++ my $repdest_fallback = ""; ++ + # decode the URI + @repuris = split(',', $repuri); + + for $repuri (@repuris) + { +- $uri = URI->new($repuri); +- if (!defined($uri) || +- !defined($uri->scheme) || +- $uri->opaque eq "") ++ my $raw_address = check_uri($repuri); ++ if ($raw_address eq "") + { +- print STDERR "$progname: can't parse reporting URI for domain $domain\n"; + next; + } +- +- $repdest = $uri->opaque; +- my $report_maxbytes = $report_maxbytes_global; +- +- # check for max report size +- if ($repdest =~ m/^(\S+)!(\d{1,15})([kmgt])?$/i) ++ else + { +- $repdest = $1; +- $report_maxbytes = $2; +- if ($3) ++ my $domain_orgdom = $suffix->get_root_domain(lc($domain)); ++ my $address = $raw_address; ++ $address =~ s/!\d{1,15}([kmgt])?$//i; ++ my $repdestdomain = $address; ++ $repdestdomain =~ s/.*@//; ++ my $repdest_orgdom = $suffix->get_root_domain(lc($repdestdomain)); ++ ++ if (defined($domain_orgdom) && defined($repdest_orgdom) && $domain_orgdom eq $repdest_orgdom) ++ { ++ if (check_size_restriction($raw_address, $reportsize)) ++ { ++ $repdest .= $address . ", "; ++ } ++ else ++ { ++ $repdest_fallback .= $address . ", "; ++ } ++ } ++ else + { +- my $letter = lc($3); +- if ($letter eq 'k') ++ # validate external report destinations: ++ my $replaced = 0; # external address replaced ++ my $authorized = 0; # external address authorized ++ my $temprepuri; ++ my $res = Net::DNS::Resolver->new(udp_timeout => 15); ++ my $reply = $res->query("$domain._report._dmarc.$repdestdomain", "TXT"); ++ if ($reply) + { +- $report_maxbytes = $report_maxbytes * 1024; ++ foreach my $txt ($reply->answer) ++ { ++ next unless $txt->type eq "TXT"; ++ my @parts = split(';', $txt->txtdata); ++ my $type = shift @parts; ++ next unless $type =~ m/^\s*v\s*=\s*DMARC1\s*/; ++ $authorized = 1; ++ # just for debugging: ++ if ($txt->txtdata ne "v=DMARC1") ++ { ++ print STDERR "$progname: DEBUG: $domain._report._dmarc.$repdestdomain: query answer: ", $txt->txtdata, "\n"; ++ } ++ foreach my $parts (@parts) ++ { ++ if ($parts =~ m/^\s*rua\s*=/) ++ { ++ $replaced = 1; ++ $parts =~ s/^\s*rua\s*=\s*//; ++ foreach my $tempuri (split(',', $parts)) ++ { ++ $raw_address = check_uri($tempuri); ++ if ($raw_address eq "") ++ { ++ next; ++ } ++ my $uridomain = lc($raw_address); ++ $uridomain =~ s/.*@//; ++ $uridomain =~ s/!\d{15}([kmgt])?$//; ++ if ($repdestdomain eq $uridomain) ++ { ++ $address =~ s/!\d([kmgt])?$//i; ++ if ($verbose) ++ { ++ print STDERR "$progname: adding new reporting URI for domain $domain: $address\n"; ++ } ++ if (check_size_restriction($raw_address, $reportsize)) ++ { ++ $repdest .= $address . ", "; ++ } ++ else ++ { ++ $repdest_fallback .= $address . ", "; ++ } ++ } ++ else ++ { ++ if ($verbose) ++ { ++ print STDERR "$progname: ignoring new reporting URI due to differing host parts: $repdestdomain != $uridomain!\n"; ++ } ++ } ++ } ++ # there should be only one part with "rua=", so stop here ++ last; ++ } ++ } ++ # there should be only one TXT record starting with "v=DMARC1", so stop here ++ last; ++ } + } +- if ($letter eq 'm') ++ else + { +- $report_maxbytes = $report_maxbytes * 1048576; ++ switch ($res->errorstring) ++ { ++ case "NXDOMAIN" { } # definitely not authorized ++ case "SERVFAIL" { $authorized = 1; } # not a definite answer, so be kind ++ case "query timed out" { $authorized = 1; } # not a definite answer, so be kind ++ else { $authorized = 1; } # for now we authorize anything else ++ } + } +- if ($letter eq 'g') ++ ++ if ($authorized && !$replaced) + { +- $report_maxbytes = $report_maxbytes * (2**30); ++ ++ $repdest .= $address . ", "; + } +- if ($letter eq 't') ++ elsif (!$authorized) + { +- $report_maxbytes = $report_maxbytes * (2**40); ++ if ($verbose) ++ { ++ print STDERR "$progname: $domain is NOT authorized to send reports to $address, dropping address! (" . $res->errorstring . ")\n"; ++ } ++ next; + } + } + } ++ } ++ $repdest =~ s/, $//; ++ $repdest_fallback =~ s/, $//; + +- # Test mode, just report what would have been done +- if ($testmode) ++ # Test mode, just report what would have been done ++ if ($testmode) ++ { ++ if ($repdest ne "") + { + print STDERR "$progname: would email $domain report for " . +- "$rowcount records to " . $uri->opaque . "\n"; ++ "$rowcount records to $repdest\n"; + } +- # ensure a scheme is present +- elsif (!defined($uri->scheme)) ++ elsif ($repdest_fallback ne "") + { +- if ($verbose >= 2) +- { +- print STDERR "$progname: unknown URI scheme in '$repuri' for domain $domain\n"; +- } +- next; ++ print STDERR "$progname: would email an error report for " . ++ "$domain to $repdest_fallback\n"; + } +- # send/post report +- elsif ($uri->scheme eq "mailto") ++ } ++ else ++ { ++ if ($repdest ne "") + { +- my $datestr; +- my $report_id; +- +- if (!open($zipin, $zipfile)) +- { +- print STDERR "$progname: can't read zipped report for $domain: $!\n"; +- next; +- } ++ # send out the report: ++ $boundary = hostfqdn() . "/" . time(); + +- $boundary = "report_section"; +- +- $report_id = $domain . "-" . $now . "@" . $repdom; +- $datestr = strftime("%a, %e %b %Y %H:%M:%S %z (%Z)", +- localtime); ++ my $report_id = $domain . "-" . $now . "@" . $repdom; ++ my $datestr = strftime("%a, %e %b %Y %H:%M:%S %z (%Z)", localtime); + + $mailout = "To: $repdest\n"; + $mailout .= "From: $repemail\n"; +- $mailout .= "Subject: Report Domain: " . $domain . " Submitter: " . $repdom . " Report-ID: " . $report_id . "\n"; ++ $mailout .= "Subject: Report Domain: " . $domain . "\n"; ++ $mailout .= " Submitter: " . $repdom . "\n"; ++ $mailout .= " Report-ID: " . $report_id . "\n"; + $mailout .= "X-Mailer: " . $progname . " v" . $version ."\n"; + $mailout .= "Date: " . $datestr . "\n"; + $mailout .= "Message-ID: <$report_id>\n"; +@@ -898,52 +1069,100 @@ foreach (@$domainset) + $mailout .= "Content-Disposition: attachment; filename=\"$zipfile\"\n"; + $mailout .= "Content-Transfer-Encoding: base64\n"; + $mailout .= "\n"; ++ $mailout .= $encoded_report; ++ $mailout .= "\n"; ++ $mailout .= "--$boundary--\n"; ++ $smtpstatus = "sent"; ++ $smtpfail = 0; ++ if (!$smtp->mail($repemail) || ++ !$smtp->to(split(', ', $repdest), {SkipBad => 1 }) || ++ !$smtp->data() || ++ !$smtp->datasend($mailout) || ++ !$smtp->dataend()) ++ { ++ $smtpfail = 1; ++ $smtpstatus = "failed to send"; ++ } + +- while (read($zipin, $buf, 60*57)) ++ if ($verbose || $smtpfail) + { +- $mailout .= encode_base64($buf); ++ # now perl voodoo: ++ $answer = ${${*$smtp}{'net_cmd_resp'}}[1] || $smtp->message() || 'unknown error'; ++ chomp($answer); ++ print STDERR "$progname: $smtpstatus report for $domain to $repdest ($answer)\n"; + } + ++ $smtp->reset(); ++ } ++ elsif ($repdest_fallback ne "") ++ { ++ # send error report to $repdest_fallback: ++ if ($verbose) ++ { ++ print STDERR "$progname: emailing an error report for $domain to $repdest_fallback\n"; ++ } ++ $boundary = hostfqdn() . "/" . time(); ++ ++ my $report_id = $domain . "-" . $now . "@" . $repdom; ++ my $datestr = strftime("%a, %e %b %Y %H:%M:%S %z (%Z)", localtime); ++ ++ $mailout = "To: $repdest_fallback\n"; ++ $mailout .= "From: $repemail\n"; ++ $mailout .= "Subject: Error Report Domain: " . $domain . " Submitter: " . $repdom . " Report-ID: " . $report_id . "\n"; ++ $mailout .= "X-Mailer: " . $progname . " v" . $version ."\n"; ++ $mailout .= "Date: " . $datestr . "\n"; ++ $mailout .= "Message-ID: <$report_id>\n"; ++ $mailout .= "Auto-Submitted: auto-generated\n"; ++ $mailout .= "MIME-Version: 1.0\n"; ++ $mailout .= "Content-Type: multipart/report;\n"; ++ $mailout .= " report-type=delivery-status;\n"; ++ $mailout .= " boundary=\"$boundary\"\n"; ++ $mailout .= "\n"; ++ $mailout .= "This is a MIME-encapsulated message.\n"; ++ $mailout .= "\n"; ++ $mailout .= "--$boundary\n"; ++ $mailout .= "Content-Description: DMARC Notification\n"; ++ $mailout .= "Content-Type: text/plain\n"; ++ $mailout .= "\n"; ++ $mailout .= "This is a DMARC error report from host " . hostfqdn() . ".\n"; ++ $mailout .= "\n"; ++ $mailout .= "I'm sorry to have to inform you that a DMARC aggregate report\n"; ++ $mailout .= "could not be delivered to any of your URIs mentioned in your DMARC\n"; ++ $mailout .= "DNS resource records because of size limitations.\n"; ++ $mailout .= "\n"; ++ $mailout .= "--$boundary\n"; ++ $mailout .= "Content-Description: DMARC Error Report\n"; ++ $mailout .= "Content-Type: text/plain\n"; ++ $mailout .= "\n"; ++ $mailout .= "Report-Date: " . strftime("%a, %b %e %Y %H:%M:%S %z (%Z)", localtime()) . "\n"; ++ $mailout .= "Report-Domain: $domain\n"; ++ $mailout .= "Report-ID: $report_id\n"; ++ $mailout .= "Report-Size: $reportsize\n"; ++ $mailout .= "Submitter: $repdom\n"; ++ $mailout .= "Submitting-URI: $repdest_fallback\n"; + $mailout .= "\n"; + $mailout .= "--$boundary--\n"; +- my $reportsize = length($mailout); +- +- if ($reportsize > $report_maxbytes) ++ $smtpstatus = "sent"; ++ $smtpfail = 0; ++ if (!$smtp->mail($repemail) || ++ !$smtp->to(split(', ', $repdest_fallback), { SkipBad => 1 }) || ++ !$smtp->data() || ++ !$smtp->datasend($mailout) || ++ !$smtp->dataend()) + { +- # XXX -- generate an error report here +- print STDERR "$progname: report was too large ($reportsize bytes) per limitation of URI " . $uri->opaque . " for domain $domain\n"; ++ $smtpfail = 1; ++ $smtpstatus = "failed to send"; + } +- else +- { +- $smtpstatus = "sent"; +- $smtpfail = 0; +- if (!$smtp->mail($repemail) || +- !$smtp->to($repdest) || +- !$smtp->data() || +- !$smtp->datasend($mailout) || +- !$smtp->dataend()) +- { +- $smtpfail = 1; +- $smtpstatus = "failed to send"; +- } + +- if ($verbose || $smtpfail) +- { +- # now perl voodoo: +- $answer = ${${*$smtp}{'net_cmd_resp'}}[1] || $smtp->message() || 'unknown error'; +- chomp($answer); +- print STDERR "$progname: $smtpstatus report for $domain to $repdest ($answer)\n"; +- } ++ if ($verbose || $smtpfail) ++ { ++ # now perl voodoo: ++ $answer = ${${*$smtp}{'net_cmd_resp'}}[1] || $smtp->message() || 'unknown error'; ++ chomp($answer); ++ print STDERR "$progname: $smtpstatus failure notice for report for $domain to $repdest ($answer)\n"; + } + + $smtp->reset(); +- +- close($zipin); +- } +- else +- { +- print STDERR "$progname: unsupported reporting URI scheme " . $uri->scheme . " for domain $domain\n"; +- next; + } + } + diff --git a/network/opendmarc/patches/z04_moreHeadersFailureReportVsBeta1.patch b/network/opendmarc/patches/z04_moreHeadersFailureReportVsBeta1.patch new file mode 100644 index 000000000000..23dc08f467d5 --- /dev/null +++ b/network/opendmarc/patches/z04_moreHeadersFailureReportVsBeta1.patch @@ -0,0 +1,352 @@ +--- a/opendmarc/opendmarc.c 2016-12-21 18:01:21.322036404 +0100 ++++ b/opendmarc/opendmarc.c 2016-12-21 18:01:48.893181823 +0100 +@@ -2057,6 +2057,7 @@ + char *apolicy = NULL; + char *aresult = NULL; + char *adisposition = NULL; ++ char *deliveryresult = NULL; + char *hostname = NULL; + char *authservid = NULL; + char *spfaddr; +@@ -2774,6 +2775,154 @@ + } + + /* ++ ** Enact policy based on DMARC results. ++ */ ++ ++ result = DMARC_RESULT_ACCEPT; ++ ++ switch (policy) ++ { ++ case DMARC_POLICY_ABSENT: /* No DMARC record found */ ++ case DMARC_FROM_DOMAIN_ABSENT: /* No From: domain */ ++ aresult = "none"; ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_ACCEPT; ++ break; ++ ++ case DMARC_POLICY_NONE: /* Alignment failed, but policy is none: */ ++ aresult = "fail"; /* Accept and report */ ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_ACCEPT; ++ break; ++ ++ case DMARC_POLICY_PASS: /* Explicit accept */ ++ aresult = "pass"; ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_ACCEPT; ++ break; ++ ++ case DMARC_POLICY_REJECT: /* Explicit reject */ ++ aresult = "fail"; ++ ++ if (conf->conf_overridemlm != NULL && ++ (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || ++ (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) ++ { ++ if (conf->conf_dolog) ++ { ++ syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", ++ dfc->mctx_jobid, dfc->mctx_fromdomain); ++ } ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_OVRD_MAILING_LIST; ++ } ++ else ++ { ++ if (conf->conf_rejectfail && random() % 100 < pct) ++ { ++ snprintf(replybuf, sizeof replybuf, ++ "rejected by DMARC policy for %s", pdomain); ++ ++ status = dmarcf_setreply(ctx, DMARC_REJECT_SMTP, ++ DMARC_REJECT_ESC, replybuf); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_setreply() failed", ++ dfc->mctx_jobid); ++ } ++ ++ ret = SMFIS_REJECT; ++ result = DMARC_RESULT_REJECT; ++ } ++ ++ if (conf->conf_copyfailsto != NULL) ++ { ++ status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_addrcpt() failed", ++ dfc->mctx_jobid); ++ } ++ } ++ } ++ ++ break; ++ ++ case DMARC_POLICY_QUARANTINE: /* Explicit quarantine */ ++ aresult = "fail"; ++ ++ if (conf->conf_overridemlm != NULL && ++ (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || ++ (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) ++ { ++ if (conf->conf_dolog) ++ { ++ syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", ++ dfc->mctx_jobid, dfc->mctx_fromdomain); ++ } ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_OVRD_MAILING_LIST; ++ } ++ else ++ { ++ if (conf->conf_rejectfail && random() % 100 < pct && ++ conf->conf_holdquarantinedmessages) ++ { ++ snprintf(replybuf, sizeof replybuf, ++ "quarantined by DMARC policy for %s", ++ pdomain); ++ ++ status = smfi_quarantine(ctx, replybuf); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_quarantine() failed", ++ dfc->mctx_jobid); ++ } ++ ++ ret = SMFIS_ACCEPT; ++ result = DMARC_RESULT_QUARANTINE; ++ } ++ ++ if (conf->conf_copyfailsto != NULL) ++ { ++ status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); ++ if (status != MI_SUCCESS && conf->conf_dolog) ++ { ++ syslog(LOG_ERR, "%s: smfi_addrcpt() failed", ++ dfc->mctx_jobid); ++ } ++ } ++ } ++ ++ break; ++ ++ default: ++ aresult = "temperror"; ++ ret = SMFIS_TEMPFAIL; ++ result = DMARC_RESULT_TEMPFAIL; ++ break; ++ } ++ ++ /* prepare human readable dispositon string for later processing */ ++ switch (result) ++ { ++ case DMARC_RESULT_REJECT: ++ adisposition = "reject"; ++ deliveryresult = "reject"; ++ break; ++ ++ case DMARC_RESULT_QUARANTINE: ++ adisposition = "quarantine"; ++ deliveryresult = "policy"; ++ break; ++ ++ default: ++ adisposition = "none"; ++ deliveryresult = "delivered"; ++ break; ++ } ++ ++ /* + ** Generate a failure report. + */ + +@@ -2931,8 +3080,11 @@ + "Auth-Failure: dmarc\n"); + + dmarcf_dstring_printf(dfc->mctx_afrf, +- "Authentication-Results: %s; dmarc=fail header.from=%s\n", +- authservid, ++ "Authentication-Results: %s;\n", ++ authservid); ++ dmarcf_dstring_printf(dfc->mctx_afrf, ++ " dmarc=%s (p=%s dis=%s) header.from=%s\n", ++ aresult, apolicy, adisposition, + dfc->mctx_fromdomain); + + dmarcf_dstring_printf(dfc->mctx_afrf, +@@ -2949,6 +3101,20 @@ + cc->cctx_host); + + dmarcf_dstring_printf(dfc->mctx_afrf, ++ "Source-Port: %u\n", ++ cc->cctx_ip.ss_family == AF_INET6 ? ntohs(((struct sockaddr_in6*) &cc->cctx_ip)->sin6_port) : ntohs(((struct sockaddr_in*) &cc->cctx_ip)->sin_port)); ++ ++ dmarcf_dstring_printf(dfc->mctx_afrf, ++ "Identity-Alignment: %s%s%s\n", ++ align_dkim == DMARC_POLICY_DKIM_ALIGNMENT_PASS ? "dkim" : "", ++ ((align_dkim == DMARC_POLICY_DKIM_ALIGNMENT_PASS) && (align_spf == DMARC_POLICY_SPF_ALIGNMENT_PASS)) ? ", " : ((align_dkim != DMARC_POLICY_DKIM_ALIGNMENT_PASS) && (align_spf != DMARC_POLICY_SPF_ALIGNMENT_PASS)) ? "none" : "", ++ align_spf == DMARC_POLICY_SPF_ALIGNMENT_PASS ? "spf" : ""); ++ ++ dmarcf_dstring_printf(dfc->mctx_afrf, ++ "Delivery-Result: %s\n", ++ deliveryresult); ++ ++ dmarcf_dstring_printf(dfc->mctx_afrf, + "Reported-Domain: %s\n\n", + dfc->mctx_fromdomain); + +@@ -3015,151 +3181,6 @@ + } + } + +- /* +- ** Enact policy based on DMARC results. +- */ +- +- result = DMARC_RESULT_ACCEPT; +- +- switch (policy) +- { +- case DMARC_POLICY_ABSENT: /* No DMARC record found */ +- case DMARC_FROM_DOMAIN_ABSENT: /* No From: domain */ +- aresult = "none"; +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_ACCEPT; +- break; +- +- case DMARC_POLICY_NONE: /* Alignment failed, but policy is none: */ +- aresult = "fail"; /* Accept and report */ +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_ACCEPT; +- break; +- +- case DMARC_POLICY_PASS: /* Explicit accept */ +- aresult = "pass"; +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_ACCEPT; +- break; +- +- case DMARC_POLICY_REJECT: /* Explicit reject */ +- aresult = "fail"; +- +- if (conf->conf_overridemlm != NULL && +- (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || +- (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) +- { +- if (conf->conf_dolog) +- { +- syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", +- dfc->mctx_jobid, dfc->mctx_fromdomain); +- } +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_OVRD_MAILING_LIST; +- } +- else +- { +- if (conf->conf_rejectfail && random() % 100 < pct) +- { +- snprintf(replybuf, sizeof replybuf, +- "rejected by DMARC policy for %s", pdomain); +- +- status = dmarcf_setreply(ctx, DMARC_REJECT_SMTP, +- DMARC_REJECT_ESC, replybuf); +- if (status != MI_SUCCESS && conf->conf_dolog) +- { +- syslog(LOG_ERR, "%s: smfi_setreply() failed", +- dfc->mctx_jobid); +- } +- +- ret = SMFIS_REJECT; +- result = DMARC_RESULT_REJECT; +- } +- +- if (conf->conf_copyfailsto != NULL) +- { +- status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); +- if (status != MI_SUCCESS && conf->conf_dolog) +- { +- syslog(LOG_ERR, "%s: smfi_addrcpt() failed", +- dfc->mctx_jobid); +- } +- } +- } +- +- break; +- +- case DMARC_POLICY_QUARANTINE: /* Explicit quarantine */ +- aresult = "fail"; +- +- if (conf->conf_overridemlm != NULL && +- (dmarcf_checkhost(cc->cctx_host, conf->conf_overridemlm) || +- (dmarcf_checkip((struct sockaddr *)&cc->cctx_ip, conf->conf_overridemlm)))) +- { +- if (conf->conf_dolog) +- { +- syslog(LOG_INFO, "%s: overriding policy for mail from %s: MLM", +- dfc->mctx_jobid, dfc->mctx_fromdomain); +- } +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_OVRD_MAILING_LIST; +- } +- else +- { +- if (conf->conf_rejectfail && random() % 100 < pct && +- conf->conf_holdquarantinedmessages) +- { +- snprintf(replybuf, sizeof replybuf, +- "quarantined by DMARC policy for %s", +- pdomain); +- +- status = smfi_quarantine(ctx, replybuf); +- if (status != MI_SUCCESS && conf->conf_dolog) +- { +- syslog(LOG_ERR, "%s: smfi_quarantine() failed", +- dfc->mctx_jobid); +- } +- +- ret = SMFIS_ACCEPT; +- result = DMARC_RESULT_QUARANTINE; +- } +- +- if (conf->conf_copyfailsto != NULL) +- { +- status = dmarcf_addrcpt(ctx, conf->conf_copyfailsto); +- if (status != MI_SUCCESS && conf->conf_dolog) +- { +- syslog(LOG_ERR, "%s: smfi_addrcpt() failed", +- dfc->mctx_jobid); +- } +- } +- } +- +- break; +- +- default: +- aresult = "temperror"; +- ret = SMFIS_TEMPFAIL; +- result = DMARC_RESULT_TEMPFAIL; +- break; +- } +- +- /* prepare human readable dispositon string for later processing */ +- switch (result) +- { +- case DMARC_RESULT_REJECT: +- adisposition = "reject"; +- break; +- +- case DMARC_RESULT_QUARANTINE: +- adisposition = "quarantine"; +- break; +- +- default: +- adisposition = "none"; +- break; +- } +- + if (conf->conf_dolog) + { + syslog(LOG_INFO, "%s: %s %s", dfc->mctx_jobid, diff --git a/network/opendmarc/patches/z06_use_envdomain_SPF_logging.patch b/network/opendmarc/patches/z06_use_envdomain_SPF_logging.patch new file mode 100644 index 000000000000..3a702fe8c055 --- /dev/null +++ b/network/opendmarc/patches/z06_use_envdomain_SPF_logging.patch @@ -0,0 +1,13 @@ +diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c +index f5c30f9..29f3f93 100644 +--- a/opendmarc/opendmarc.c ++++ b/opendmarc/opendmarc.c +@@ -2542,7 +2542,7 @@ mlfi_eom(SMFICTX *ctx) + &used_mfrom); + if (used_mfrom == TRUE) + { +- use_domain = dfc->mctx_envfrom; ++ use_domain = dfc->mctx_envdomain; + spf_mode = DMARC_POLICY_SPF_ORIGIN_MAILFROM; + } + else diff --git a/network/opendmarc/slack-desc b/network/opendmarc/slack-desc new file mode 100644 index 000000000000..3085453bb31c --- /dev/null +++ b/network/opendmarc/slack-desc @@ -0,0 +1,19 @@ +# HOW TO EDIT THIS FILE: +# The "handy ruler" below makes it easier to edit a package description. +# Line up the first '|' above the ':' following the base package name, and +# the '|' on the right side marks the last column you can put a character in. +# You must make exactly 11 lines for the formatting to be correct. It's also +# customary to leave one space after the ':' except on otherwise blank lines. + + |-----handy-ruler------------------------------------------------------| +opendmarc: opendmarc (DMARC milter and library) +opendmarc: +opendmarc: OpenDMARC is a free open source software implementation of the DMARC +opendmarc: specification. +opendmarc: +opendmarc: Homepage: http://www.trusteddomain.org/opendmarc/ +opendmarc: +opendmarc: +opendmarc: +opendmarc: +opendmarc: -- cgit v1.2.3