diff --git a/opendmarc/opendmarc-config.h b/opendmarc/opendmarc-config.h
index 9f6b8e4..46bf84a 100644
--- a/opendmarc/opendmarc-config.h
+++ b/opendmarc/opendmarc-config.h
@@ -37,6 +37,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 d315b56..2bed138 100644
--- a/opendmarc/opendmarc.c
+++ b/opendmarc/opendmarc.c
@@ -176,6 +176,7 @@ struct dmarcf_config
char * conf_ignorelist;
char ** conf_trustedauthservids;
char ** conf_ignoredomains;
+ struct list * conf_overridemlm;
};
/* LIST -- basic linked list of strings */
@@ -1229,6 +1230,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,64 +2995,96 @@ mlfi_eom(SMFICTX *ctx)
break;
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, domain);
}
-
- ret = SMFIS_REJECT;
- result = DMARC_RESULT_REJECT;
+ aresult = "pass";
+ 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)
+ aresult = "fail";
+
+ 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);
+ }
}
}
break;
case DMARC_POLICY_QUARANTINE: /* Explicit quarantine */
- aresult = "fail";
-
- if (conf->conf_rejectfail && conf->conf_holdquarantinedmessages && 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 because of MLM",
+ dfc->mctx_jobid, domain);
}
-
+ aresult = "pass";
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)
+ aresult = "fail";
+
+ if (conf->conf_rejectfail && conf->conf_holdquarantinedmessages && random() % 100 < pct)
{
- syslog(LOG_ERR, "%s: smfi_addrcpt() failed",
- dfc->mctx_jobid);
+ 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);
+ }
}
}
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 98ec20e..f312fb0 100755
--- a/reports/opendmarc-reports.in
+++ b/reports/opendmarc-reports.in
@@ -92,6 +92,8 @@ my $ipaddr;
my $fromdomain;
my $envdomain;
my $dkimdomain;
+my $reason;
+my $comment;
my $repdest;
@@ -593,6 +595,8 @@ foreach (@$domainset)
while ($dbi_a = $dbi_s->fetchrow_arrayref())
{
undef $msgid;
+ undef $reason;
+ undef $comment;
if (defined($dbi_a->[0]))
{
@@ -640,6 +644,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"; }
}
@@ -681,6 +691,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$comment>\n";
+ }
+ print $tmpout " \n";
+ }
print $tmpout " \n";
print $tmpout " \n";
print $tmpout " \n";