diff --git a/opendmarc/opendmarc.c b/opendmarc/opendmarc.c index 076d393..bdad10d 100644 --- a/opendmarc/opendmarc.c +++ b/opendmarc/opendmarc.c @@ -2061,6 +2061,7 @@ mlfi_eom(SMFICTX *ctx) char *apolicy = NULL; char *aresult = NULL; char *adisposition = NULL; + char *deliveryresult = NULL; char *hostname = NULL; char *authservid = NULL; char *spfaddr; @@ -2743,6 +2744,154 @@ mlfi_eom(SMFICTX *ctx) } /* + ** 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 */ + case DMARC_POLICY_NONE: /* Accept and report */ + aresult = "none"; + 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, domain); + } + 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, domain); + } + ret = SMFIS_ACCEPT; + result = DMARC_RESULT_OVRD_MAILING_LIST; + } + else + { + if (conf->conf_rejectfail && conf->conf_holdquarantinedmessages && 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) + { + 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; + } + + if (conf->conf_dolog) + { + syslog(LOG_INFO, "%s: %s %s", dfc->mctx_jobid, + dfc->mctx_fromdomain, aresult); + } + + /* ** Generate a failure report. */ @@ -2910,8 +3059,11 @@ mlfi_eom(SMFICTX *ctx) "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, @@ -2927,6 +3079,20 @@ mlfi_eom(SMFICTX *ctx) cc->cctx_ipstr, 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); @@ -2992,151 +3158,6 @@ mlfi_eom(SMFICTX *ctx) } } - /* - ** 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 */ - case DMARC_POLICY_NONE: /* Accept and report */ - aresult = "none"; - 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, domain); - } - 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, domain); - } - ret = SMFIS_ACCEPT; - result = DMARC_RESULT_OVRD_MAILING_LIST; - } - else - { - if (conf->conf_rejectfail && conf->conf_holdquarantinedmessages && 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) - { - 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, - dfc->mctx_fromdomain, aresult); - } - /* if the final action isn't TEMPFAIL or REJECT, add an A-R field */ if (ret != SMFIS_TEMPFAIL && ret != SMFIS_REJECT) {