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\n"; + } + print $tmpout " \n"; + } print $tmpout " \n"; print $tmpout " \n"; print $tmpout " \n";